【Codeforces】Round #543 (Div 2) 解题报告

Posted on 2019-03-08 20:30  opethrax  阅读(397)  评论(0编辑  收藏  举报

前言:今天改题时结识了FlyWhite学长,下午与他聊了许久。近来生活充斥着各种不确定因素,真的是不能飘啊。学长建议我每场cf都打,把所有的题都补完并且写题解,所以就拿前天改完的Round #543来开刀吧。

对自己的吐槽:还是一如既往地只在ABC徘徊,数组又开小了...并且没有做出C... 

 

Problem A. Technogoblet of Fire


 

题目大意:有n个人来自k个学校,告诉你n个人的能力。现在每个学校派出最强的那个人去参赛。但是现在要求有m个人必须要参赛,但他们可能不是自己学校最强的,可以把他们放进新学校里。问最少的新学校数目。

数据范围:所有数字均在100以内

根据题意先保存每个学校最强的人的能力是多少,然后对每一个人判断他是不是最强的那个,不是就++ans

代码:不贴了。

 

Problem B. Mike and Children


 

题目大意:Mike有n个大小不同的糖果,现在Mike决定给一些孩子每个人2个糖果,如果孩子们拿到的2个糖果大小的和不相同,孩子们就会不开心。现在问Mike最多可以使多少个孩子开心。

数据范围:1≤n≤1000,1≤糖果大小≤105

因为大小不同,所以每一个糖果和其他n-1个糖果一共可以得到n-1个大小, O(N2)计算所有的结果放在一个桶中,取最大值。

代码:不贴了。

 

Problem C. System Testing


 

题目大意:有一台评测机,同一时刻可以评测k个程序,评测每个程序的1个样例需要1s。给出n个程序的样例数(运行时间),测完一个就测下一个。定义一个程序是“有趣的”当这个程序在评测第x个样例时,系统显示评测进度为x%。求有趣的程序的数量。

系统评测进度的计算方法:已评测程序数/总程序数*100 四舍五入。

数据范围:1≤n≤1000,1≤k≤100,1≤每个程序的样例数≤150

题意比较简单,但是代码不太好写(码力不足请充值)。数据范围较小,所以可以考虑安照时间来1s 1s地做。

代码:

 1 #include<stdio.h>
 2 
 3 const int N=1050;
 4 const int K=150;
 5 
 6 int n,k,ans=0;
 7 int cnt,dcnt,d;
 8 int a[N];
 9 struct task{
10     int num,time;
11 }p[N];
12 bool vis[N];
13 
14 int main(){
15     scanf("%d%d",&n,&k);
16     for(int i=1;i<=n;i++)scanf("%d",&a[i]);
17     while(dcnt<n){
18         for(int i=1;i<=k;i++){
19             if(p[i].num==0||p[i].time==a[p[i].num]){
20                 if(p[i].num)++dcnt;
21                 if(cnt<n){
22                     p[i].num=++cnt;
23                     p[i].time=1;
24                 }
25                 else p[i].num=0;
26             }
27             else ++p[i].time;
28         }
29         d=(int)((double)100*dcnt/n+0.5);
30         for(int i=1;i<=k;i++)if(p[i].time==d)vis[p[i].num]=1;
31     }
32     for(int i=1;i<=n;i++)ans+=vis[i];
33     printf("%d",ans);
34     return 0;
35 }
~

 

Problem D. Diana and Liana


 

题目大意:现在有一条藤蔓,上面有k种花,共m朵。有一台机器可以把每k朵花做成一个花环,当藤蔓上花不足k朵是就停止工作不再制造花环了。有n个人需要n个花环。但是其中一个人想要自己指定的s朵花做成的花环(其他的n-1个人不在乎自己的花环长啥样,有花环就行了),他可以从藤蔓上去掉一些花使藤蔓的某一段刚好具有他想要的s朵花并且被做成一个花环。现在求他需要去掉多少朵花,是哪些花。

花环上花的顺序无关紧要,

数据范围:1≤n,k,m≤5*105,k*n≤m,1≤s≤k

我们先计算出最多可以去掉的花数量d,在一个k+d的区间内统计k种花的数量看看有没有我们需要的那s朵,如果有就暴力算出可以去掉的花的下标,否则我们跳到下一个区间[k*i+1,k*i+k+d],并且再次统计各种花的数量,判断是否符合条件。利用一个类似莫队的方法,把区间k个k个单位的移动,把区间外的“吐”掉,再把区间内的“吃”掉。时间复杂度O(N),每朵花只被计算一次。

代码:

 1 #include<stdio.h>
 2 #include<algorithm>
 3 using namespace std;
 4 const int N=500050;
 5 
 6 int m,k,n,s;
 7 int a[N],b[N];
 8 int len;
 9 int cnt[N],ok;
10 struct node{
11     int v,pos;
12     bool operator <(const node _)const{
13         return v<_.v;
14     }
15 }c[N];
16 bool vis[N];
17 int main(){
18     scanf("%d%d%d%d",&m,&k,&n,&s);
19     for(int i=1;i<=m;i++)scanf("%d",&a[i]);
20     for(int i=1;i<=s;i++)scanf("%d",&b[i]),--cnt[b[i]];
21     for(int i=1;i<=N;i++)if(cnt[i]<0)++ok;
22     len=m-n*k;
23     len+=k;
24     int l=0,r=0,p=0,t;
25     int ll=0,rr=0;
26     while(p*k+len<=m){
27         ll=l; rr=r;
28         l=p*k+1; r=p*k+len;
29         t=min(l-1,rr);
30         for(int i=ll;i<=t;i++){
31             if(!i)continue;
32             --cnt[a[i]];
33             if(cnt[a[i]]==-1)++ok;
34         }
35         t=max(l,rr+1);
36         for(int i=t;i<=r;i++){
37             ++cnt[a[i]];
38             if(cnt[a[i]]==0)--ok;
39         }
40         ++p;
41         if(ok==0){
42             len-=k;
43             printf("%d\n",len);
44             if(!len)return 0; 
45             for(int i=l;i<=r;i++)c[i]=(node){a[i],i};
46             sort(b+1,b+s+1);
47             sort(c+l,c+r);
48             int pb=1,pc=l;
49             while(1){
50                 if(c[pc].v!=b[pb]){printf("%d ",c[pc].pos); --len; ++pc;}
51                 if(len==0)return 0;
52                 if(c[pc].v==b[pb]){++pc,++pb;}
53             }
54             return 0;
55         }
56     }
57     printf("-1");
58     return 0;
59 }
~

 

Problem E. Once in a casino


 

题目大意:对一个长度为n的数字a,我们可以对他的相邻两位(i&i+1,1<=i<n)加1或者减1。0不能减1,9不能加1。求数字a能不能通过操作变成数字b,并且输出方案(步数大于1×105就只输出前1×105步)

数据范围:2≤n≤105

官方公告:由于技术原因此题可能比F题要难...

贪心,从最边上开始,如果现在这位比b小就++,比b大就--,相邻的那一为逢0就加,逢9就减。如此递归,同时记录方案。

然后再算一遍,相同的方法,不过不需要记录步数了,还有些细节需要处理。

代码:

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<iostream>
 4 using namespace std;
 5 const int N=100050;
 6 
 7 int n;
 8 char s[N];
 9 int a[N],b[N],x[N],y[N];
10 int cnt;
11 long long ans;
12 struct step{
13     int k,d;
14 }st[N*10];
15 
16 int min(int _,int __){return _<__?_:__;}
17 void over(){
18     printf("-1");
19     exit(0);
20 }
21 void add(int i);
22 void sub(int i);
23 void add(int i){
24     if(i==n)over();
25     if(a[i+1]==9)sub(i+1);
26     st[++cnt]=(step){i,1};
27     ++a[i]; ++a[i+1];
28 }
29 void sub(int i){
30     if(i==n)over();
31     if(a[i+1]==0)add(i+1);
32     st[++cnt]=(step){i,-1};
33     --a[i]; --a[i+1];
34 }
35 int main(){
36     scanf("%d",&n);
37     scanf("%s",s+1);
38     for(int i=1;i<=n;i++)a[i]=x[i]=s[i]-'0';
39     scanf("%s",s+1);
40     for(int i=1;i<=n;i++)b[i]=y[i]=s[i]-'0';
41     for(int i=1;i<n;i++){
42         while(a[i]<b[i]&&cnt<1e5)add(i);
43         while(a[i]>b[i]&&cnt<1e5)sub(i);
44     }
45     int t;
46     for(int i=1;i<n;i++){
47         if(x[i]<0){
48             t=-x[i];
49             ans+=t;
50             x[i]+=t; x[i+1]+=t;
51         }
52         if(x[i]>9){
53             t=x[i]-9;
54             ans+=t;
55             x[i]-=t; x[i+1]-=t;
56         }
57         while(x[i]<y[i])++x[i],++x[i+1],++ans;
58         while(x[i]>y[i])--x[i],--x[i+1],++ans;
59     }
60     if(x[n]!=y[n])over();
61     printf("%I64d\n",ans);
62     for(int i=1;i<=min(cnt,100000);i++){
63         printf("%d %d\n",st[i].k,st[i].d);
64     }
65     return 0;
66 }
~

Problem F. Compress String


 

题目大意:有一个长度为n的字符串S,可以把其划分成若干字串:S->t1t2t3...tk-1tk啊,对于每一个字串ti的划分代价是

·若ti长度为1,代价为a

·若ti是t1t2t3...ti-1ti的字串,代价为b

求字符串的最小划分代价。

数据范围:1≤n,a,b≤5000

 

暴力求出每个位置i上以i结尾的字符串是前面字符串的字串长度最大是多少,然后dp即可。

代码:

 1 #include<stdio.h>
 2 #include<iostream>
 3 using namespace std;
 4 
 5 const int N=5050;
 6 
 7 int n,a,b;
 8 int f[N];
 9 char s[N];
10 int z[N];
11 
12 int zfunc(int t)
13 {
14     int ans=0;
15     z[t]=0;
16     int l=n+1;
17     int r=n+1;
18     for(int i=t-1;i>=1;i--)
19     {
20         z[i]=0;
21         if(r<=i)z[i]=min(i-r+1,z[t-l+i]);
22         while(i-z[i]>0&&s[i-z[i]]==s[t-z[i]])
23         z[i]++;
24         if(z[i]&&i-z[i]<r)r=i-z[i]+1,l=i;
25         ans=max(ans,min(z[i],t-i));
26     }
27     return    ans;
28 }
29 
30 int    main(){
31     scanf("%d%d%d",&n,&a,&b);
32     scanf("%s",s+1);
33     f[1]=a;
34     for(int i=2;i<=n;i++)
35     {
36         int lng=zfunc(i);
37         f[i]=f[i-1]+a;
38         if(lng)f[i]=min(f[i],f[i-lng]+b);
39     }
40     printf("%d",f[n]);
41     return 0;
42 }
~