CF1481X Codeforces Round #699
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 // vectorpos[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 如果$p 可以证明这种方法一定成立,有如下性质 $rest[i]\le num[i+1]$且$rest[i]\le num[i]$,$rest[D]=0$ $rest$最后一定减少到0,容易发现一定存在$rest[i]\le p 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