"华为杯"华南理工大学程序设计竞赛(同步赛) H、K、M(还没写)题解

 

"华为杯"华南理工大学程序设计竞赛(同步赛)_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ (nowcoder.com)

 

H

对于没有处理特殊情况(Sppac 国国王 Capps 居住在此街道),DP很好想。对于一个点为根的子树,如果这个点不选,它的孩子都要选;如果这个点选,它的孩子可以选/不选。

f[d][1] += max(f[chi][0], f[chi][1]);
f[d][0] += f[chi][1];

 

然后对于特殊情况,对于这条边,可以让这条边的两个点为0(除此之外没有变化使得值发生变化)。

需要执行:所有点依次转成根的操作。就是从原始设置的根节点开始往下遍历,从V的父节点U为根节点,变为V为根节点。

对于V子树以外的内容,就是U子树-V子树。

对于V=0:因为V=0,所以U=1,对于U子树-V子树,这种情况的V可以为0或者1,所以是

f[chi][0] + ( f1[d][1] - max(f[chi][0], f[chi][1]) )
对于V=1:因为V=1,所以U=0或者1。对于U子树-V子树,当U=1,这种情况的V可以为0或者1;当U=0,这种情况的V只能为1,所以是
f[chi][1] + max( ( f1[d][1] - max(f[chi][0], f[chi][1]) , f1[d][0] - f[chi][1] )

 

    int root_use_1, root_use_0;
    for (int chi:child[d])
    {
        root_use_1 = f1[d][1] - max(f[chi][0], f[chi][1]);
        root_use_0 = f1[d][0] - f[chi][1];
        f1[chi][0] = f[chi][0] + root_use_1;
        f1[chi][1] = f[chi][1] + max(root_use_1, root_use_0);

        find_f1(chi);
    }

 

 

 

 

造样例,比如一些随机的图。我用cyaron。手写出每种一条边可以不用的情况。

找到一些能判断错误的方法,比如特殊情况的值(一条边可以两个点为0)肯定大于等于没有处理特殊情况的值。

事实上,当图的点=8,我就能找出我的错误代码跑出来的错误样例了。

1
8
1 2
2 3
2 4
2 8
4 5
4 6
4 7

 

 

 

 

PS:如果写对拍的话,造比较小的样例,然后全部遍历找最大值,n个点,就是2^n(每个点只能01),然后乘上n-1条边就是2^n*(n-1),好写,2^20*20=20,971,520。

 

 

 

 

 

  1 #include <cstdio>
  2 #include <cstdlib>
  3 #include <cstring>
  4 #include <cmath>
  5 #include <stdbool.h>
  6 #include <string>
  7 #include <algorithm>
  8 #include <iostream>
  9 #include <sstream>
 10 #include <ctime>
 11 #include <stack>
 12 #include <vector>
 13 #include <queue>
 14 #include <set>
 15 #include <map>
 16 using namespace std;
 17 #define LL long long
 18 
 19 const int maxn=4e5+10;
 20 
 21 ///0 destroy ; 1 remain, has this point
 22 
 23 
 24 int a[maxn], fa[maxn], edge[maxn][2];
 25 int f[maxn][2], f1[maxn][2], result[maxn];
 26 vector<int> adj[maxn],child[maxn];
 27 bool vis[maxn];
 28 
 29 void dfs(int d)
 30 {
 31     vis[d]=1;
 32     f[d][0]=1;
 33     f[d][1]=0;
 34     for (int chi:adj[d])
 35         if (!vis[chi])
 36         {
 37             dfs(chi);
 38             child[d].push_back(chi);
 39             fa[chi]=d;
 40 
 41             f[d][1] += max(f[chi][0], f[chi][1]);
 42             f[d][0] += f[chi][1];
 43         }
 44 }
 45 
 46 void find_f1(int d)
 47 {
 48     int root_use_1, root_use_0;
 49     for (int chi:child[d])
 50     {
 51         root_use_1 = f1[d][1] - max(f[chi][0], f[chi][1]);
 52         root_use_0 = f1[d][0] - f[chi][1];
 53         f1[chi][0] = f[chi][0] + root_use_1;
 54         f1[chi][1] = f[chi][1] + max(root_use_1, root_use_0);
 55 
 56         find_f1(chi);
 57     }
 58 
 59     ///max(f[1][0],f[1][1]) 加了这个居然提高了
 60 
 61     for (int chi:child[d])
 62     {
 63 
 64         result[chi] = max(f1[d][1], f1[d][0] + max(0, f[chi][0] - f[chi][1]) ) ;
 65 
 66         ///test ======
 67 
 68         //result[chi] = max( max(f[1][0],f[1][1]), max(f1[d][1], f1[d][0] + max(0, f[chi][0] - f[chi][1]) ) );
 69         /*
 70         if (result[chi] < max(f[1][0],f[1][1]))
 71         {
 72             cout<<"wrong child "<<chi<<endl;
 73             exit(-1); ///是存在这种情况的
 74         }
 75         */
 76     }
 77 
 78 }
 79 
 80 int main()
 81 {
 82     int T,n,i,x,y,chi;
 83     scanf("%d",&T);
 84     while (T--)
 85     {
 86         scanf("%d",&n);
 87         for (i=1;i<=n;i++)
 88         {
 89             adj[i].clear();
 90             child[i].clear();
 91             vis[i]=0;
 92             fa[i]=0;
 93             f[i][0]=0, f[i][1]=0, f1[i][0]=0, f1[i][1]=0;
 94         }
 95 
 96         for (i=1;i<n;i++)
 97         {
 98             scanf("%d%d",&x,&y);
 99             adj[x].push_back(y);
100             adj[y].push_back(x);
101             edge[i][0]=x, edge[i][1]=y;
102         }
103 
104         dfs(1);
105 
106         f1[1][0]=f[1][0];
107         f1[1][1]=f[1][1];
108         find_f1(1);
109 
110         for (i=1;i<n;i++)
111         {
112             if (fa[ edge[i][0] ] == edge[i][1])
113                 chi = edge[i][0];
114             else
115                 chi = edge[i][1];
116 
117             cout << result[chi];
118             if (i==n-1)
119                 cout<<endl;
120             else
121                 cout<<" ";
122         }
123     }
124 
125     return 0;
126 }
127 /*
128 
129 wrong
130 
131 1
132 8
133 1 2
134 2 3
135 2 4
136 2 8
137 4 5
138 4 6
139 4 7
140 
141 
142 ======
143 
144 1
145 5
146 1 3
147 2 3
148 4 5
149 4 1
150 
151 ======
152 
153 1
154 6
155 2 4
156 2 6
157 1 2
158 3 5
159 3 4
160 
161 ======
162 
163 1
164 7
165 2 3
166 3 5
167 1 6
168 4 2
169 7 5
170 6 5
171 
172 ======
173 
174 10
175 
176 
177 ======
178 
179 1
180 2
181 2 1
182 
183 result:2
184 
185 ======
186 
187 1
188 6
189 1 6
190 5 6
191 3 5
192 4 3
193 2 1
194 
195 ======
196 
197 2
198 6
199 1 6
200 5 6
201 3 5
202 4 3
203 2 1
204 5
205 1 2
206 2 3
207 2 4
208 1 5
209 
210 ======
211 
212 4
213 6
214 1 6
215 5 6
216 3 5
217 4 3
218 2 1
219 5
220 1 2
221 2 3
222 2 4
223 1 5
224 6
225 1 6
226 5 6
227 3 5
228 4 3
229 2 1
230 5
231 1 2
232 2 3
233 2 4
234 1 5
235 
236 ======
237 
238 */

 

 

 

 

K

第一眼看题有点线段树的感觉。但是数值为x的数的位置,是离散的。

那么数值为x的数,用一个数据结构统计起来,然后op=1,2需要修改(删除和增加)和查找(删除的位置在哪),那么需要查找、删除、添加,都<=logn的复杂度。

set的查找和删除都是logn,而且set是unique结构,每次都排好序。

对于op=1,a[x]=y a[x]->a[x]+1,a[x]这个数的集合发生变化。

对于op=2,数值为x的集合合并到数值为x+1的集合。 这个操作是选择保留大的堆(大小大的堆),把小堆的内容移到大堆

还有因为多数据,一些初始化的时间复杂度需要注意。

写这种题,变量比较乱,变量名要写清楚。首先是写清楚变量名的意思,其次是变量名是好辨认。

(还有一些操作可以合并写的,就合并写。)

 

  1 /*
  2 num_pos: a[x]这个数在哪个set里
  3 set_value: 这个set对应是保存数值为多少的集合
  4 set_pos: 数值为y的集合,在哪个set里
  5 */
  6 #include <cstdio>
  7 #include <cstdlib>
  8 #include <cstring>
  9 #include <cmath>
 10 #include <string>
 11 #include <algorithm>
 12 #include <iostream>
 13 #include <set>
 14 #include <vector>
 15 using namespace std;
 16 #define LL long long
 17 const int maxn=6e5+10;
 18 
 19 set<int> s[maxn];
 20 int set_pos[maxn], num_pos[maxn],set_value[maxn];
 21 set<int> need_clean_set;
 22 
 23 int main()
 24 {
 25     int test_cout=0;
 26 
 27     int T,n,Q,op,x,size_1,size_2,has_1,has_2,i,cnt,xori,xadd,set_index;
 28     scanf("%d",&T);
 29     while (T--)
 30     {
 31         scanf("%d%d",&n,&Q);
 32         cnt=1;
 33         for (i=1;i<=n;i++)
 34         {
 35             s[1].insert(i);
 36             num_pos[i]=1;
 37         }
 38         set_pos[0]=1;
 39         set_value[1]=0;
 40         need_clean_set.insert(1);
 41 
 42         while (Q--)
 43         {
 44             scanf("%d%d",&op,&x);
 45             if (op==1)
 46             {
 47                 set_index = num_pos[x];
 48                 xori = set_value[ set_index ];
 49                 s[ set_index ].erase( s[ set_index ].find(x) );
 50                 xadd = xori + 1;
 51                 if (set_pos[ xadd ]==0)
 52                 {
 53                     cnt++;
 54                     set_pos[ xadd ] = cnt;
 55                     set_value[ cnt ] = xadd;
 56                 }
 57                 s[ set_pos[xadd] ].insert(x);
 58                 num_pos[x] = set_pos[ xadd ];
 59 
 60                 need_clean_set.insert(set_pos[ xori ]);
 61                 need_clean_set.insert(set_pos[ xadd ]);
 62             }
 63             else
 64             {
 65                 has_1 = (set_pos[x]!=0);
 66                 has_2 = (set_pos[x+1]!=0);
 67 
 68                 size_1 = s[ set_pos[x] ].size();
 69                 size_2 = s[ set_pos[x+1] ].size();
 70 
 71                 if (has_1!=0)
 72                 {
 73                     if (has_2==0)
 74                     {
 75                         cnt++;
 76                         set_pos[ x+1 ] = cnt;
 77                         set_value[ cnt ] = x+1;
 78                     }
 79 
 80                     need_clean_set.insert(set_pos[ x ]);
 81                     need_clean_set.insert(set_pos[ x+1 ]);
 82 
 83                     if (size_1>size_2)
 84                     {
 85                         for (int d : s[ set_pos[x+1] ])
 86                         {
 87                             s[ set_pos[x] ].insert(d);
 88                             num_pos[d] = set_pos[x];
 89                         }
 90                         set_pos[x+1]=set_pos[x];
 91                         set_pos[x]=0;
 92                         set_value[ set_pos[x+1] ] = x+1;
 93                     }
 94                     else
 95                     {
 96                         for (int d : s[ set_pos[x] ])
 97                         {
 98                             s[ set_pos[x+1] ].insert(d);
 99                             num_pos[d] = set_pos[x+1];
100                         }
101 
102                         set_pos[x]=0;
103                     }
104                 }
105             }
106         }
107 
108         if (test_cout)
109             cout<<"test begin"<<endl;
110         for (i=1;i<=n;i++)
111         {
112             cout<<set_value[ num_pos[i] ];
113             if (i==n)
114                 cout<<endl;
115             else
116                 cout<<" ";
117         }
118 
119         if (test_cout)
120             cout<<"test end"<<endl;
121 
122         int temp=2*max(n,Q);
123         for (i=1;i<=temp;i++)
124         {
125             set_pos[i]=0;
126             set_value[i]=0;
127             num_pos[i]=0;
128         }
129         for (int set_index:need_clean_set)
130             s[set_index].clear();
131     }
132 
133     return 0;
134 }
135 /*
136 
137 100
138 3 9
139 1 1
140 1 1
141 1 1
142 1 2
143 1 2
144 1 2
145 2 3
146 2 4
147 2 3
148 
149 
150 ======
151 
152 
153 
154 ======
155 
156 
157 ======
158 
159 
160 ======
161 
162 */

 

 

 

  1 ///TLE
  2 /*
  3 num_pos: a[x]这个数在哪个set里
  4 set_value: 这个set对应是保存数值为多少的集合
  5 set_pos: 数值为y的集合,在哪个set里
  6 */
  7 
  8 /*
  9 如果想改为这个两个变量的话,
 10 对于op=2,数值为y的数都需要修改。
 11 而对于用num_pos,则大堆不需要修改
 12 
 13 
 14 
 15 num_value: 下标为x的数,当前的数值
 16 set_pos: 数值为y的集合,在哪个set里
 17 */
 18 
 19 #include <cstdio>
 20 #include <cstdlib>
 21 #include <cstring>
 22 #include <cmath>
 23 #include <string>
 24 #include <algorithm>
 25 #include <iostream>
 26 #include <set>
 27 #include <vector>
 28 using namespace std;
 29 #define LL long long
 30 const int maxn=6e5+10;
 31 
 32 set<int> s[maxn];
 33 int num_value[maxn], set_pos[maxn];
 34 set<int> need_clean_set;
 35 
 36 int main()
 37 {
 38     int test_cout=0;
 39 
 40     int T,n,Q,op,x,size_1,size_2,i,cnt,yori,ynew,set_ori_index,set_new_index;
 41     scanf("%d",&T);
 42     memset(set_pos, 0, sizeof(set_pos));
 43     while (T--)
 44     {
 45         scanf("%d%d",&n,&Q);
 46         cnt=1;
 47         for (i=1;i<=n;i++)
 48         {
 49             s[1].insert(i);
 50             num_value[i]=0;
 51         }
 52         set_pos[0]=1;
 53 
 54         need_clean_set.insert(1);
 55 
 56         while (Q--)
 57         {
 58             scanf("%d%d",&op,&x);
 59             if (op==1)
 60             {
 61                 yori = num_value[x];
 62                 set_ori_index = set_pos[ yori ];
 63                 s[ set_ori_index ].erase( s[ set_ori_index ].find(x) );
 64                 ynew = yori + 1;
 65                 num_value[x] = ynew;
 66                 if (set_pos[ ynew ]==0)
 67                 {
 68                     cnt++;
 69                     set_pos[ ynew ] = cnt;
 70                 }
 71                 set_new_index = set_pos[ ynew ];
 72                 s[ set_new_index ].insert(x);
 73 
 74                 need_clean_set.insert( set_ori_index );
 75                 need_clean_set.insert( set_new_index );
 76             }
 77             else
 78             {
 79                 set_ori_index = set_pos[x];
 80                 set_new_index = set_pos[x+1];
 81                 size_1 = s[ set_ori_index ].size();
 82                 size_2 = s[ set_new_index ].size();
 83 
 84                 ///op=2 保证有元素。 -> 这样不用判断了。 size_1>0,那么size_2=0,也可以处理的
 85 
 86                     need_clean_set.insert( set_ori_index );
 87                     need_clean_set.insert( set_new_index );
 88 
 89                 if (size_1>size_2)
 90                 {
 91                     for (int d : s[ set_new_index ])
 92                         s[ set_ori_index ].insert(d);
 93                     for (int d : s[ set_ori_index ])
 94                         num_value[d] = x+1;
 95 
 96                     set_pos[x+1]=set_pos[x];
 97                     set_pos[x]=0;
 98                 }
 99                 else
100                 {
101                     for (int d : s[ set_ori_index ])
102                     {
103                         s[ set_new_index ].insert(d);
104                         num_value[d] = x+1;
105                     }
106                     set_pos[x]=0;
107                 }
108             }
109         }
110 
111         if (test_cout)
112             cout<<"test begin"<<endl;
113         for (i=1;i<=n;i++)
114         {
115             cout<<num_value[i];
116             if (i==n)
117                 cout<<endl;
118             else
119                 cout<<" ";
120         }
121 
122         if (test_cout)
123             cout<<"test end"<<endl;
124 
125         int temp=2*max(n,Q);    ///每次操作,最大数值和需要修改的set数都最多增加1。但*2,保险一点
126         for (i=1;i<=temp;i++)
127         {
128             num_value[i]=0;
129             set_pos[i]=0;
130         }
131         for (int set_ori_index:need_clean_set)
132             s[set_ori_index].clear();
133     }
134 
135     return 0;
136 }
137 /*
138 
139 100
140 3 9
141 1 1
142 1 1
143 1 1
144 1 2
145 1 2
146 1 2
147 2 3
148 2 4
149 2 3
150 
151 
152 ======
153 
154 
155 
156 ======
157 
158 
159 ======
160 
161 
162 ======
163 
164 */

 

M

这题意很好懂,总感觉过去见过类似的题。

 

posted @ 2024-04-15 02:03  congmingyige  阅读(75)  评论(0编辑  收藏  举报