C Fence Painting(構造)
有用的刷子貪心刷,沒用的刷子填在後續的有用/已存在的位置(用個棧記一下就行)
D AB Graph(圖上構造)
把邊當做三種類型,aa bb ab
m爲奇數時,隨便挑一條邊來回跑m次就行,一定是迴文的
m爲偶數時,如果存在aa or bb邊,來回跑m次;如果不存在,那麼圖中只有ab邊。由於這是一張完全圖,$n\ge 3$時一定存在兩條連續的首尾相接的ab邊,根據m/2的奇偶性即可構造出迴文串
考場後1min敲完,我真行
1 const int N1=1005; const ll inf=0x3f3f3f3f3f3f3f3fll; 2 3 int n,T,m; 4 int p[N1][N1]; 5 char str[N1]; 6 int solve() 7 { 8 for(int i=1;i<=n;i++) 9 { 10 scanf("%s",str+1); 11 for(int j=1;j<=n;j++) if(j!=i) 12 if(str[j]=='a') p[i][j]=0; else p[i][j]=1; 13 } 14 int fl; 15 if(m&1){ 16 puts("YES"); 17 for(int k=0;k<=m;k++) if(k&1) printf("%d ",1); else printf("%d ",2); 18 puts(""); 19 return 1; 20 }else{ 21 for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(i!=j) 22 { 23 if(p[i][j]==p[j][i]) 24 { 25 puts("YES"); 26 for(int k=0;k<=m;k++) if(k&1) printf("%d ",i); else printf("%d ",j); 27 puts(""); 28 return 1; 29 } 30 } 31 if(n==2) return 0; 32 for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(i!=j) 33 for(int k=1;k<=n;k++) if(k!=i&&k!=j&&p[i][j]==p[j][k]) 34 { 35 puts("YES"); 36 if((m/2)&1) 37 { 38 for(int l=0;l<=m/2;l++) if(l&1) printf("%d ",j); else printf("%d ",i); 39 for(int l=m/2+1;l<=m;l++) if(l&1) printf("%d ",j); else printf("%d ",k); 40 }else{ 41 for(int l=0;l<=m/2;l++) if(l&1) printf("%d ",k); else printf("%d ",j); 42 for(int l=m/2+1;l<=m;l++) if(l&1) printf("%d ",i); else printf("%d ",j); 43 } 44 puts(""); 45 return 1; 46 } 47 } 48 return 0; 49 } 50 51 int main() 52 { 53 freopen("a.in","r",stdin); 54 scanf("%d",&T); 55 while(T--) 56 { 57 scanf("%d%d",&n,&m); 58 int ans=solve(); 59 if(!ans) puts("NO"); 60 } 61 return 0; 62 }
E Sorting books(DP)
題目大意:給你一個序列,每次操作可以把一個數放到最後面,問最少需要多少次操作,能讓所有相同的數連在一起。$n=5e5$
大力$DP$就行
設$l[i],r[i]$分別表示數i在最左和最右邊出現的位置
設$f[i]$表示$i$到$n$中的所有$l[j]\ge i$的數中,可以保留的數字的最大數目,剩下的數都需要按照某種順序移動到右面
若$f[i]=l[a_{i}]$,如果把$a[i]$留下,那麼在$l[a_{i}]$和$r[a_{i}]$之間的其它數字都不能保留,$f[i]=f[r[a_{i}]+1]+num[a_{i}]$
如果不保留$a_{i}$,或者$f[i]$不等於$l[a_{i}]$,那麼$f[i]=f[i+1]$
最終答案是$n-f[1]$
然而會有反例,比如2 2 1 2 2 2 1 1 3,最優方案是挪最左邊的1,再把3挪到1的右邊,代價爲2,上面的DP求出的答案是3,即保留2和3挪1
這時需要單獨考慮挪走尾部一段數的情況
如果$f[i]$不等於$l[a_{i}]$,例如$f[7]$,保留7和7後面的所有1也是一種可行方案!這其實是在討論序列最後一個不動的數在哪
1 const int N1=500005; const int inf=0x3f3f3f3f; 2 3 int n,T,m; 4 int a[N1],L[N1],R[N1],num[N1],f[N1],id[N1]; 5 // vector<int>pos[N1]; 6 7 int main() 8 { 9 freopen("a.in","r",stdin); 10 scanf("%d",&n); 11 for(int i=1;i<=n;i++) scanf("%d",&a[i]); 12 for(int i=1;i<=n;i++) 13 { 14 if(!L[a[i]]) L[a[i]]=R[a[i]]=i; 15 else{ 16 R[a[i]]=i; 17 } 18 num[a[i]]++; id[i]=num[a[i]]; 19 // pos[a[i]].push_back(i); 20 } 21 int j; 22 for(int i=n;i>=1;i--) 23 { 24 f[i]=f[i+1]; 25 if(i==L[a[i]]){ 26 f[i]=max(f[i],f[R[a[i]]+1]+num[a[i]]); 27 }else{ 28 f[i]=max(f[i],num[a[i]]-id[i]+1); 29 } 30 } 31 printf("%d\n",n-f[1]); 32 return 0; 33 }
F AB Tree(DP+貪心)
題目大意:給你一棵樹,讓你在樹的每個節點上填字母,一共要填x個a,n-x個b,問如何填字母,使得根到每個節點的路徑組成的串種類最少,$n=1e5$
設根深度爲1,樹深度爲$D$,可以討論發現答案不是D就是D+1
如果答案是$D$,那麼所有相同深度的點字母一定相同,x個a能恰好覆蓋完一些深度,其它的填b
考慮用揹包$DP$的方法來驗證是否存在合法方案,設$f[j]$表示$j$能否被表示
設$val[i]$表示點的數目,$fre[i]$表示點數爲$val[i]$的深度出現的次數
如果j能被表示,則$f[j-val[i]],f[j-2*val[i]]...f[j-fre[i]*val[i]]$中有一個能被表示
貪心記錄更大的位置,再用滾動數組轉移
不同的點數最多有$O(\sqrt n)$種,DP的時間複雜度是$O(n\sqrt n)$
如果答案是$D+1$,必須有一個深度既有a又有b,而且其中的非葉節點必須填相同的字母
設$num[i],rest[i]$分別表示深度i的節點數和非葉節點數
現在開始分配a,設還剩p個a
如果$p>num[i]$,把這個深度填滿a
如果$rest[i]\le p<num[i]$,這個位置滿足分配條件
如果$p<rest[i]$,繼續看後續有沒有滿足條件的深度
可以證明這種方法一定成立,有如下性質
$rest[i]\le num[i+1]$且$rest[i]\le num[i]$,$rest[D]=0$
$rest$最後一定減少到0,容易發現一定存在$rest[i]\le p<rest[i-1] \le num[i]$
1 const int N1=100005; const int M1=455; const int inf=0x3f3f3f3f; 2 3 struct EDGE{ 4 int to[N1*2],nxt[N1*2],head[N1],cte; 5 void ae(int u,int v) 6 { cte++; to[cte]=v, nxt[cte]=head[u]; head[u]=cte; } 7 }e; 8 int n,D,X,Y,type,pre,cur; 9 int fa[N1],dep[N1],num[N1],rest[N1],col[N1]; 10 vector<int>nodes[N1]; 11 12 void dfs(int u) 13 { 14 num[dep[u]]++; D=max(D,dep[u]); nodes[dep[u]].push_back(u); 15 for(int j=e.head[u];j;j=e.nxt[j]) 16 { 17 int v=e.to[j]; 18 dep[v]=dep[u]+1; dfs(v); 19 } 20 if(!e.head[u]) rest[dep[u]]++; 21 } 22 int ok[2][N1],id[N1],from[N1],pos[N1]; 23 int freq[N1],val[N1],used[N1],m; 24 25 void print(int ans) 26 { 27 printf("%d\n",ans); 28 for(int i=1;i<=n;i++) printf("%c",(col[i]^type)+'a'); 29 puts(""); 30 } 31 void findway(int ma) 32 { 33 int now=ma,i,j,k,v; 34 while(now) 35 { 36 j=from[now], i=id[now]; 37 used[val[i]]=(now-j)/val[i]; 38 now=j; 39 } 40 for(int i=1;i<=D;i++) 41 { 42 if(!used[num[i]]) continue; 43 for(int j=0;j<nodes[i].size();j++) 44 { 45 v=nodes[i][j]; 46 col[v]=0; 47 } 48 used[num[i]]--; 49 } 50 } 51 int getx(int fl) 52 { 53 if(fl) 54 for(int i=1;i<=D;i++) 55 { 56 if(!pos[num[i]]) m++, pos[num[i]]=m, freq[m]=1, val[m]=num[i]; 57 else freq[ pos[num[i]] ]++; 58 } 59 memset(ok,0,sizeof(ok)); 60 int now; ok[0][0]=1; pre=0, cur=1; 61 for(int i=1;i<=m;i++) 62 { 63 memcpy(ok[cur],ok[pre],sizeof(ok[cur])); 64 for(int k=0;k<val[i];k++) 65 { 66 now=-1; 67 for(int j=k;j<=X;j+=val[i]) 68 { 69 if(ok[pre][j]) now=j; 70 else if(now!=-1&& val[i]*freq[i]>=j-now) 71 ok[cur][j]=1, from[j]=now, id[j]=i; 72 } 73 } 74 swap(cur,pre); 75 if(ok[pre][X]) break; 76 } 77 if(!ok[pre][X]) return 0; 78 findway(X); print(D); 79 return 1; 80 } 81 int fillma() 82 { 83 int x=X; 84 for(int i=1;i<=D;i++) 85 if(x>=rest[i]&&x<=num[i]) 86 { 87 int re=x-rest[i]; 88 for(int j=0;j<nodes[i].size();j++) 89 { 90 int v=nodes[i][j]; 91 if(e.head[v]) col[v]=0; 92 else if(re) col[v]=0, re--; 93 } 94 print(D+1); 95 return 1; 96 }else if(x>num[i]){ 97 for(int j=0;j<nodes[i].size();j++) 98 { 99 int v=nodes[i][j]; col[v]=0; 100 } 101 x-=num[i]; 102 } 103 return 0; 104 } 105 106 int main() 107 { 108 freopen("a.in","r",stdin); 109 scanf("%d%d",&n,&X); Y=n-X; if(X>Y) swap(X,Y), type^=1; 110 for(int i=2;i<=n;i++) scanf("%d",&fa[i]), e.ae(fa[i],i); 111 dep[1]=1, dfs(1); 112 for(int i=1;i<=D;i++) rest[i]=num[i]-rest[i]; 113 for(int i=1;i<=n;i++) col[i]=1; 114 if(getx(1)) return 0; 115 int ma=0, k; 116 for(int i=1;i<=D;i++) if(num[i]>ma) ma=num[i], k=i; 117 fillma(); 118 // if(!) fillend(); 119 return 0; 120 }