前言:今天改题时结识了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 }