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 }
View Code

 

 

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 }
View Code

 

 

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 }
View Code

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章