Codeforces Round #629 (Div. 3) 补题

  这次div3打的过于自闭,各种进不去页面,各种WA,各种智障思路......

  下次网络再卡试试这个,或者挂个梯子吧。

  赛后看题解发现交上去的题似乎都想麻烦了,于是决定重补一遍,洗心革面,重新做人。


Tutorial


 

A

  输入两个数a、b, 问a最少增加多少可以成为b的倍数。

  假设a是kb+m,那么只需要增加b-m,即b-a%b,特判a%b==0即可。

 1 #pragma GCC optimize(2)
 2 #include<iostream>
 3 #include<stdio.h>
 4 #include<cstdio>
 5 #include<string.h>
 6 #include<algorithm>
 7 #include<vector>
 8 #include<queue>
 9 #include<map>
10 #include<cmath>
11 #include<set>
12 #define INF 0x3f3f3f3f
13 typedef long long ll;
14 using namespace std;
15 inline void read(int &p)
16 {
17     p=0;int flag=1;char c=getchar();
18     while(!isdigit(c)) {if(c=='-') flag=-1;c=getchar();}
19     while(isdigit(c)) {p=p*10+c-'0';c=getchar();}p*=flag;
20 }
21 int a,b,q;
22 int main()
23 {
24     #ifdef LOCAL
25     freopen("in.txt","r",stdin);
26     freopen("out.txt","w",stdout);
27     #endif
28     read(q);
29     while(q--){
30         cin>>a>>b;
31         if(a%b==0){
32             cout<<0<<endl;
33         }
34         else{
35             cout<<b-a%b<<endl;
36         }
37     }
38     return 0;
39 }
View Code

 

B

  长度为n的字符串,包含两个b,其余为a,求所有构成的字符串按字典序排序后第k个字符串。

  比赛时的想法是先确定第一个b的位置,n是1e5,于是用了二分确定位置,然后再确定第二个b的位置就是容易的。在二分上浪费了巨多时间,还因为爆int交了两发wa,赛后看题解发现根本不用二分直接从右向左遍历即可,心态爆炸......

 1 #pragma GCC optimize(2)
 2 #include<iostream>
 3 #include<stdio.h>
 4 #include<cstdio>
 5 #include<string.h>
 6 #include<algorithm>
 7 #include<vector>
 8 #include<queue>
 9 #include<map>
10 #include<cmath>
11 #include<set>
12 #define INF 0x3f3f3f3f
13 typedef long long ll;
14 using namespace std;
15 inline void read(int &p)
16 {
17     p=0;int flag=1;char c=getchar();
18     while(!isdigit(c)) {if(c=='-') flag=-1;c=getchar();}
19     while(isdigit(c)) {p=p*10+c-'0';c=getchar();}p*=flag;
20 }
21 int q,n,k;
22 int main()
23 {
24     #ifdef LOCAL
25     freopen("in.txt","r",stdin);
26     freopen("out.txt","w",stdout);
27     #endif
28     read(q);
29     while(q--){
30         int l,r;
31         read(n),read(k);
32         for(int i=n-1;i>=1;i--){
33             if(k<=n-i){
34                 l=i;
35                 r=n-k+1;
36                 break;
37             }
38             k-=n-i;
39         }
40         for(int i=1;i<=n;i++){
41             if(i==l||i==r){
42                 putchar('b');
43             }
44             else{
45                 putchar('a');
46             }
47         }
48         putchar('\n');
49         
50     }
51     return 0;
52 }
View Code

 

C

  定义一种只包含0 1 2的数字,这种数字的xor运算为对应每一位的和mod3。给出这种数字x,求a b,使得a xor b==x,并且max(a,b)最小。

  这道题比赛时思路应该是正确的,但是写的比较乱。按照每一位考虑,要得到0,需要0 0或者1 2,可以发现对于得到0选取0 0永远是最优的。要得到1需要1 0或2 2,遇到要得到1,我们选取1 0永远更优。要得到2,可以选取1 1和0 2。可以发现要得到1,对应位上的两个数有1 0和0 1的差别,得到2也有0 2,2 0,1 1的差别。这些都会对max(a,b)造成影响。我们只需要在第一次遇到1时给a对应位赋1,b对应位赋0,之后遇到1时给a对应位赋0,b对应位赋1。遇到1之前,遇到2时给a对应位赋1,b对应位赋1。遇到1之后,遇到2时给a对应位赋0,b对应位赋2,就可以使max(a,b)最小,不容易说明白,但其实观察样例就能发现。

 1 #pragma GCC optimize(2)
 2 #include<iostream>
 3 #include<stdio.h>
 4 #include<cstdio>
 5 #include<string.h>
 6 #include<algorithm>
 7 #include<vector>
 8 #include<queue>
 9 #include<map>
10 #include<cmath>
11 #include<set>
12 #define INF 0x3f3f3f3f
13 typedef long long ll;
14 using namespace std;
15 inline void read(int &p)
16 {
17     p=0;int flag=1;char c=getchar();
18     while(!isdigit(c)) {if(c=='-') flag=-1;c=getchar();}
19     while(isdigit(c)) {p=p*10+c-'0';c=getchar();}p*=flag;
20 }
21 int main()
22 {
23     #ifdef LOCAL
24     freopen("in.txt","r",stdin);
25     freopen("out.txt","w",stdout);
26     #endif
27     int q;
28     read(q);
29     while(q--){
30         int n;
31         read(n);
32         string a,b;
33         char v;
34         bool flag=true;
35         for(int i=1;i<=n;i++){
36             v=getchar();
37             if(v=='2'){
38                 if(flag){
39                     a+='1';
40                     b+='1';
41                 }
42                 else{
43                     a+='0';
44                     b+='2';
45                 }
46             }
47             else if(v=='0'){
48                 a+='0';
49                 b+='0';
50             }
51             else{
52                 if(flag){
53                     a+='1';
54                     b+='0';
55                     flag=false;
56                 }
57                 else{
58                     a+='0';
59                     b+='1';
60                 }
61             }
62         }
63         cout<<a<<"\n"<<b<<'\n';
64     }
65     return 0;
66 }
View Code

D

  题意有点复杂,不重复了...

  因为前面的智障思路,写到这道题的时候已经没什么时间了,并且这道题还想出了更智障的思路......

  正解:首先容易想到最多只有3种颜色。如果所有数字都一样,那么只需要一种颜色;如果n是偶数,那么[1,2,1,2,...]一定是符合条件的;考虑n为奇数,如果存在两个相同的数字相邻,只需要把这两个数字看成一个数字,按照n为偶数染色就好了;否则,可以看作n个不同的数绕成一个环,前n-1个数按照偶数情况染色,最后一个数染成3就符合情况。

 

 1 #pragma GCC optimize(2)
 2 #include<iostream>
 3 #include<stdio.h>
 4 #include<cstdio>
 5 #include<string.h>
 6 #include<algorithm>
 7 #include<vector>
 8 #include<queue>
 9 #include<map>
10 #include<cmath>
11 #include<set>
12 #define INF 0x3f3f3f3f
13 typedef long long ll;
14 using namespace std;
15 inline void read(int &p)
16 {
17     p=0;int flag=1;char c=getchar();
18     while(!isdigit(c)) {if(c=='-') flag=-1;c=getchar();}
19     while(isdigit(c)) {p=p*10+c-'0';c=getchar();}p*=flag;
20 }
21 int main()
22 {
23     #ifdef LOCAL
24     freopen("in.txt","r",stdin);
25     freopen("out.txt","w",stdout);
26     #endif
27     int q;
28     read(q);
29     while(q--){
30         int n;
31         read(n);
32         bool flag=true;
33         int pos=0;
34         int pre,now,f;
35         read(pre);
36         f=pre;
37         for(int i=2;i<=n;i++){
38             read(now);
39             if(now==pre){
40                 pos=i-1;
41             }
42             else{
43                 flag=false;
44             }
45             pre=now;
46         }
47         if(flag){
48             printf("1\n");
49             for(int i=1;i<=n;i++) printf("1 ");
50             putchar('\n');
51         }
52         else if(n%2==0){
53             printf("2\n");
54             for(int i=1;i<=n;i++)
55                 if(i%2) printf("1 ");
56                 else printf("2 ");
57             putchar('\n');
58         }
59         else{
60             if(pos){
61                 printf("2\n");
62                 for(int i=1;i<=pos;i++)
63                     if(i%2) printf("1 ");
64                     else printf("2 ");
65                 for(int i=pos+1;i<=n;i++)
66                     if(i%2) printf("2 ");
67                     else printf("1 ");
68                 putchar('\n');
69             }
70             else{
71                 if(f==now){
72                     printf("2\n");
73                     for(int i=1;i<n;i++)
74                         if(i%2) printf("1 ");
75                         else printf("2 ");
76                     printf("1 \n");
77                 }
78                 else{
79                     printf("3\n");
80                     for(int i=1;i<n;i++)
81                         if(i%2) printf("1 ");
82                         else printf("2 ");
83                     printf("3\n");
84                 }
85             }
86         }
87     }
88     return 0;
89 }
View Code

 


 E

  首先根据题意,如果一个节点的若干个儿子都是被询问的对象,那么只要这个节点在所选的路上,这几个被询问的节点就满足要求。所以问题可以转化为:所有被询问节点的父亲节点是否在一条路上。根据这样的思路,我们可以选取被询问节点中最深层的一个节点,其他节点都变成他们的父亲节点,那么只要我们选定的这个节点在其他所有节点的子树上,就可以满足条件。

  采用dfs序维护这样的关系,首先将根节点压入栈中,并记录入栈时间tin[v],之后把他的所有子节点也压入栈,如此往复,每个节点在出栈时记录出栈时间tout[v]。在维护这样的关系的同时,我们顺便可以记录每个节点的深度与父亲节点。我们可以发现这样的关系:如果v是u的儿子,那么v比u后入栈,u比v后出栈,即tin[u]<=tin[v]&&tout[u]>=tout[v],这样我们就可以实现上述想法。根据题解还学会了vector的一些骚操作。

 

 1 #pragma GCC optimize(2)
 2 #include<iostream>
 3 #include<stdio.h>
 4 #include<cstdio>
 5 #include<string.h>
 6 #include<algorithm>
 7 #include<vector>
 8 #include<queue>
 9 #include<map>
10 #include<cmath>
11 #include<set>
12 #define INF 0x3f3f3f3f
13 typedef long long ll;
14 using namespace std;
15 inline void read(int &p)
16 {
17     p=0;int flag=1;char c=getchar();
18     while(!isdigit(c)) {if(c=='-') flag=-1;c=getchar();}
19     while(isdigit(c)) {p=p*10+c-'0';c=getchar();}p*=flag;
20 }
21 vector<int> p,d;
22 vector<int> tin,tout;
23 vector<vector<int> > tree;
24 int n,m,t;
25 void dfs(int v,int par,int deep){
26     p[v]=par;
27     d[v]=deep;
28     tin[v]=t++;
29     for(int i=0;i<tree[v].size();i++){
30         int to=tree[v][i];
31         if(to==par) continue;
32         dfs(to,v,deep+1);
33     }
34     tout[v]=t++;
35 }
36 
37 bool judge(int u,int v){
38     return tin[u]>=tin[v]&&tout[u]<=tout[v];
39 }
40 
41 int main()
42 {
43     #ifdef LOCAL
44     freopen("in.txt","r",stdin);
45     freopen("out.txt","w",stdout);
46     #endif
47     read(n),read(m);
48     p=d=tin=tout=vector<int>(n);
49     tree=vector<vector<int> >(n);
50     for(int i=0;i<n-1;i++){
51         int x,y;
52         read(x),read(y);
53         x--;y--;
54         tree[x].push_back(y);
55         tree[y].push_back(x);
56     }
57     dfs(0,-1,0); 
58     while(m--){
59         int k;
60         read(k);
61         vector<int> ver(k);
62         for(int i=0;i<k;i++){
63             read(ver[i]);
64             ver[i]--;
65         }
66         int u=ver[0];
67         for(int i=0;i<ver.size();i++){
68             int _=ver[i];
69             if(d[_]>d[u]) u=_;
70         }
71         for(int i=0;i<ver.size();i++){
72             int _=ver[i];
73             if(_==u||p[_]==-1) continue;
74             ver[i]=p[_];
75         }
76         bool flag=true;
77         for(int i=0;i<ver.size();i++){
78             int _=ver[i];
79             if(!judge(u,_)){
80                 flag=false;
81                 break;
82             }
83         }
84         if(flag) printf("YES\n");
85         else printf("NO\n");
86     }
87     return 0;
88 }
View Code

 


 F

 遍历数组中的每一种元素,求出k个数都变成该元素的最小步数,再求出整个数组的最小步数即可。首先将数组从小到大排序,并用一个pair类型数组维cnt护每种元素的个数cnt[i].first是元素值,cnt[i].second是元素个数。令val=cnt[i].first,那么要使k个数都变成val,数组中需要通过增大或减小变成val的元素数目为need=max(0,k-cnt[i].second)。要求出need个数需要的操作数,我们可以把need分为两部分,需要通过增大变成val的元素数目needl,需要通过减小变成val的元素数目needr。因为我们在维护cnt之前已经把原数组从小到大排序,所以needl个数都在i的左侧,needr个数都在i右侧。令prefcnt[i]为小于等于cnt[i].first的元素的数目,prefsum[i]为prefcnt[i]个数的和。可以先令needl=min(need,prefcnt[i-1]),needr=max(0,need-needl),那么needl个数要变成val,根据题目的变换规则,需要把i左侧的元素全部变成val-1,再让这needl个数加一,所需操作数为prefcnt[i-1]*(val-1)-presum[i-1]+needl,对于needr个数同样可以通过sufcnt数组和sufsum数组维护比cnt[i].first大的元素数目和它们的和。注意开long long。

  过程比较繁琐,写完调bug也很心累....

 

  1 #pragma GCC optimize(2)
  2 #include<iostream>
  3 #include<stdio.h>
  4 #include<cstdio>
  5 #include<string.h>
  6 #include<algorithm>
  7 #include<vector>
  8 #include<queue>
  9 #include<map>
 10 #include<cmath>
 11 #include<set>
 12 #define INF 1e18
 13 #define x first
 14 #define y second
 15 typedef long long ll;
 16 using namespace std;
 17 inline void read(int &p)
 18 {
 19     p=0;int flag=1;char c=getchar();
 20     while(!isdigit(c)) {if(c=='-') flag=-1;c=getchar();}
 21     while(isdigit(c)) {p=p*10+c-'0';c=getchar();}p*=flag;
 22 }
 23 int main()
 24 {
 25     #ifdef LOCAL
 26     freopen("in.txt","r",stdin);
 27     freopen("out.txt","w",stdout);
 28     #endif
 29     int n,k;
 30     read(n),read(k);
 31     vector<int> a(n);
 32     for(int i=0;i<n;i++) read(a[i]);
 33     sort(a.begin(),a.end());
 34     vector<pair<int,int> > cnt;
 35     for(int i=0;i<n;i++){
 36         if(cnt.empty()||cnt.back().x!=a[i]){
 37             cnt.push_back({a[i],1});
 38         }
 39         else{
 40             cnt.back().y++;
 41         }
 42     }
 43     vector<int> prefcnt,sufcnt;
 44     vector<ll> prefsum,sufsum;
 45     for(int i=0;i<cnt.size();i++){
 46         ll cursum=cnt[i].x*1ll*cnt[i].y;
 47         int curcnt=cnt[i].y;
 48         if(prefsum.empty()){
 49             prefcnt.push_back(curcnt);
 50             prefsum.push_back(cursum);
 51         }
 52         else{
 53             prefcnt.push_back(prefcnt.back()+curcnt);
 54             prefsum.push_back(prefsum.back()+cursum);
 55         }
 56     }
 57     for(int i=cnt.size()-1;i>=0;i--){
 58         ll cursum=cnt[i].x*1ll*cnt[i].y;
 59         int curcnt=cnt[i].y;
 60         if(sufsum.empty()){
 61             sufcnt.push_back(curcnt);
 62             sufsum.push_back(cursum);
 63         }
 64         else{
 65             sufsum.push_back(sufsum.back()+cursum);
 66             sufcnt.push_back(sufcnt.back()+curcnt);
 67         }
 68     }
 69 
 70     reverse(sufsum.begin(), sufsum.end());
 71     reverse(sufcnt.begin(), sufcnt.end());
 72 
 73     ll ans=INF;
 74     for(int i=0;i<cnt.size();i++){
 75         int val=cnt[i].x;
 76         int need=max(0,k-cnt[i].y);
 77         int needl=0,needr;
 78         ll curans=0;
 79         if(i>0) needl=min(need,prefcnt[i-1]);
 80         needr=max(0,need-needl);
 81         if(i>0&&needl>0){
 82             curans+=(val-1)*1ll*prefcnt[i-1]-prefsum[i-1];
 83             curans+=needl;
 84         }
 85         if(i+1<cnt.size()&&needr>0){
 86             curans+=sufsum[i+1]-(val+1)*1ll*sufcnt[i+1];
 87             curans+=needr;
 88         }
 89         
 90         ans=min(ans,curans);
 91 
 92         curans=0;
 93         needr=0;
 94         if(i+1<cnt.size()) needr=min(need,sufcnt[i+1]);
 95         needl=max(0,need-needr);
 96         if(i>0&&needl>0){
 97             curans+=(val-1)*1ll*prefcnt[i-1]-prefsum[i-1];
 98             curans+=needl;
 99         }
100         if(i+1<cnt.size()&&needr>0){
101             curans+=sufsum[i+1]-(val+1)*1ll*sufcnt[i+1];
102             curans+=needr;
103         }
104         ans=min(ans,curans);
105     }
106     printf("%lld",ans);
107     return 0;
108 }
View Code

 


佛系补题,用时三天......

舒服了。。。

 

posted @ 2020-03-29 23:49  DinoMax  阅读(176)  评论(0)    收藏  举报