• Codeforces Round #555 (Div. 3)[1157]题解


    不得不说这场div3是真的出的好,算得上是从我开始打开始最有趣的一场div3。因为自己的号全都蓝了,然后就把不经常打比赛的dreagonm的号借来打这场,然后...比赛结束rank11(帮dreagonm上蓝果然没有食言qwq)。

    qwq

    (震惊...HA省A队CF青名...)

    upd:system test之后的最终排名是rank9,dreagonmTQL!!!

    CF1157A Reachable Numbers

    水题,分析一下不难发现不超过(10)次就会少一位,然后(10^9)范围内的数可以到达的数个数显然不多,不妨大力模拟,然后把出现的数扔进set,最后输出set.size()即可。(开到(10^6)不会T也不会有被卡次数的危险)

    #include<bits/stdc++.h>
    
    using namespace std;
    
    int n;
    
    set<int>s;
    
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<=1e6;i++){
    		s.insert(n);
    		n+=1;
    		while(n%10==0){
    			n/=10;
    		}
    	}
    	printf("%d
    ",(int)s.size());
    	return 0;
    }
    

    CF1157B Long Number

    注意是选择一段连续的区间,把区间内的数全部替换为映射值。不难发现可能会增大也有可能减小,为了让数尽可能变大,考虑采取贪心的策略,找到第一个能使得原数变大的位置(l),然后从这个位置向后找,找到第一个会变小的位置(r),则变化区间为([l,r))

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int N=2e5+5;
    
    int n;
    
    char s[N];
    
    int a[15];
    
    char ans[N],t[N];
    
    int fir,lst;
    
    int main(){
    	scanf("%d",&n);
    	scanf("%s",s+1);
    	for(int i=1;i<=9;i++){
    		scanf("%d",&a[i]);
    	}
    	for(int i=1;i<=n;i++){
    		if(s[i]-'0'<a[s[i]-'0']){
    			fir=i;
    			break;
    		}
    	}
    	if(!fir){
    		printf("%s
    ",s+1);
    		return 0;
    	}
    	for(lst=fir;lst<=n;lst++){
    		if(s[lst]-'0'>a[s[lst]-'0']){
    			lst--;
    			break;
    		}
    	}
    	if(lst>n){
    		lst=n;
    	}
    	for(int i=fir;i<=lst;i++){
    		s[i]='0'+a[s[i]-'0'];
    	}
    	printf("%s
    ",s+1);
    	return 0;
    }
    

    CF1157C Increasing Subsequence

    C1

    显然,贪心就行了,因为选完小的还能选大的,答案不会变劣。

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int N=2e5+5;
    
    int n;
    
    int a[N],now,l,r;
    
    char op[N];
    
    int cntop;
    
    int main(){
    	scanf("%d",&n);
    	l=1;r=n;
    	for(int i=1;i<=n;i++){
    		scanf("%d",&a[i]);
    	}
    	while(l<=r){
    		if(a[l]>now&&a[r]>now){
    			if(a[l]<a[r]){
    				op[++cntop]='L';
    				now=a[l];
    				l++;
    			}
    			else{
    				op[++cntop]='R';
    				now=a[r];
    				r--;
    			}
    		}
    		else if(a[l]>now){
    			op[++cntop]='L';
    			now=a[l];
    			l++;
    		}
    		else if(a[r]>now){
    			op[++cntop]='R';
    			now=a[r];
    			r--;
    		}
    		else{
    			break;
    		}
    	}
    	printf("%d
    %s
    ",cntop,op+1);
    	return 0;
    }
    

    C2

    C1不一样的是,不保证任意两两不同。那么考虑和C1不同的点,不难发现在于两端的数的大小关系多了相等的情况,其他的也没什么不同。显然,如果两端相同,之后就只能从一边选,那么对于这种情况直接贪心选择最多的一边选即可。

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int N=2e5+5;
    
    int n;
    
    int a[N],now,l,r;
    
    char op[N];
    
    int cntop;
    
    int main(){
    	scanf("%d",&n);
    	l=1;r=n;
    	for(int i=1;i<=n;i++){
    		scanf("%d",&a[i]);
    	}
    	while(l<=r){
    		if(a[l]>now&&a[r]>now){
    			if(a[l]==a[r]){
    				for(int i=l+1,j=r-1;;i++,j--){
    					if(a[i]<=a[i-1]){
    						while(a[r]>now){
    							op[++cntop]='R';
    							now=a[r];
    							r--;
    						}
    						break;
    					}
    					else if(a[j]<=a[j+1]){
    						while(a[l]>now){
    							op[++cntop]='L';
    							now=a[l];
    							l++;
    						}
    						break;
    					}
    				}
    			}
    			else if(a[l]<a[r]){
    				op[++cntop]='L';
    				now=a[l];
    				l++;
    			}
    			else{
    				op[++cntop]='R';
    				now=a[r];
    				r--;
    			}
    		}
    		else if(a[l]>now){
    			op[++cntop]='L';
    			now=a[l];
    			l++;
    		}
    		else if(a[r]>now){
    			op[++cntop]='R';
    			now=a[r];
    			r--;
    		}
    		else{
    			break;
    		}
    	}
    	printf("%d
    %s
    ",cntop,op+1);
    	return 0;
    }
    

    CF1157D N Problems During K Days

    这题挺有趣的,要求构造(k)个正整数和为(n),且满足对于(1leq i<k)(a_i<a_{i+1}leq2a_i)。从约束条件入手,分析下界可知,如果还有(k)个数需要确定,而第一个数为(i),那么这(k)个数在和最小的情况下分别是(i,i+1,cdots,i+k-1),由此可得下界。分析上界可知,和最大的情况下分别是(i,2i,2^2i,cdots,2^{k-1}i),和为((2^k-1)i),由此可得上界。因为第一个数可以无穷大但不能小于(1),所以约束主要是在下界,那么尽可能避免下界不合法即可,所以对于每个位置(i),使(a_i)尽可能小即可。以样例为例,n=8 k=3时,如果(a_1=1),那么和最大为(1+2+4=7),显然过小。如果(a_1=2),和最小为(2+3+4=9),又过大了,那么就不合法。n=9 k=4时,和最小为(1+2+3+4=10),亦过大。所以总的思路就是在和的上界不小于剩余的值的情况下选择最小的(a_i=max(lceilLargefrac{n}{2^{k-i+1}-1} ormalsize ceil,a_{i-1}+1)),然后判断在取得最小可能值的情况下会不会有下界不合法即可(因为取值已经保证上界合法)。

    Trick:double在存储(2)的次方是可以得到精确值,用pow(2.0,x)就能得到精确的(2^x),就不需要再担心(large2^{10^5})过大的问题。

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int N=1e5+5;
    
    int n,k;
    
    long long sum(int u){
    	return 1LL*u*(u+1)/2;
    }
    
    double sumb(int u){
    	return pow(2.0,u)-1;
    }
    
    int a[N];
    
    int main(){
    	scanf("%d%d",&n,&k);
    	for(int i=1;i<=k;i++){
    		int rep=max(a[i-1]+1,(int)ceil((double)n/sumb(k+1-i)));
    		if(sum(rep+k-i)-sum(rep-1)>n){
    			printf("NO
    ");
    			return 0;
    		}
    		a[i]=rep;
    		n-=rep;
    	}
    	printf("YES
    ");
    	for(int i=1;i<=k;i++){
    		printf("%d%c",a[i]," 
    "[i==k]);
    	}
    	return 0;
    }
    

    CF1157E Minimum Array

    题目意思是给出(a,b)两个数组,要求给(b)重新排序,使得(c_i=(a_i+b_i)mod{n})的字典序最小。显然字典序是由高位开始比较的,所以显然可以贪心解决,只需要让前面的(c_i)尽可能小即可。由模运算的性质可知,每次找到不小于(a_imod{n})的相反数的最小数即可,如果不存在就选所有数的最小值。然后整个过程用multiset维护(b)数组即可。

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int N=2e5+5;
    
    int n;
    
    int a[N],b[N];
    
    multiset<int>s;
    
    int c[N];
    
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++){
    		scanf("%d",&a[i]);
    	}
    	for(int i=1;i<=n;i++){
    		scanf("%d",&b[i]);
    		s.insert(b[i]);
    	}
    	for(int i=1;i<=n;i++){
    		int rep=(n-a[i])%n;
    		auto ite=s.lower_bound(rep);
    		if(ite==s.end()){
    			ite=s.begin();
    		}
    		c[i]=*ite;
    		s.erase(ite);
    	}
    	for(int i=1;i<=n;i++){
    		printf("%d%c",(a[i]+c[i])%n," 
    "[i==n]);
    	}
    	return 0;
    }
    

    CF1157F Maximum Balanced Circle

    题目意思是从给定的(n)个数中选尽可能多的数,使得这些数按照某种顺序放到环上后,相邻两个数的差不超过(1)。显然对于这道题可以先桶排,然后考虑选出的数中最小的数,如果选出的数最小值和最大值分别为(l,r),那么一定需要按照(l,l+1,cdots,r,r-1,cdots,l)排序,否则相邻相差就会大于(1)。那么不难发现,对于(forall iin(l,r),cnt_i>1)。由此,可以考虑枚举(l),求出最大的(r),显然,对于(l'in(l,r]),如果将选出的数范围改为([l',r]),那么答案一定会比([l,r])劣,所以枚举的时间复杂度为(O(max a_i))。然后考虑一下细节,处理(l=r,l+1=r,l+1<r)三种情况即可。

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int N=2e5+5;
    
    int n,a[N],c[N],sum[N];
    
    int ans;
    
    int mx,l,r;
    
    int query(int u,int v){
    	return sum[v]-sum[u-1];
    }
    
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++){
    		scanf("%d",&a[i]);
    		++c[a[i]];
    	}
    	for(int i=1;i<2e5;i++){
    		if(c[i]&&c[i+1]){
    			ans=i;
    			break;
    		}
    	}
    	if(!ans){
    		for(int i=1;i<=2e5;i++){
    			if(c[i]>mx){
    				ans=i;
    				mx=c[i];
    			}
    		}
    		printf("%d
    ",mx);
    		for(int i=1;i<=mx;i++){
    			printf("%d%c",ans," 
    "[i==mx]);
    		}
    		return 0;
    	}
    	for(int i=1;i<=2e5;i++){
    		sum[i]=sum[i-1]+c[i];
    	}
    	for(int i=1,j;i<=2e5;){
    		if(!c[i]||!c[i+1]){
    			i++;
    			continue;
    		}
    		else if(c[i+1]==1){
    			if(mx<query(i,i+1)){
    				mx=query(i,i+1);
    				l=i;r=i+1;
    			}
    			i++;
    		}
    		else{
    			for(j=i+1;j<=n;j++){
    				if(c[j]>1){
    					continue;
    				}
    				else{
    					break;
    				}
    			}
    			if(!c[j]){
    				--j;
    			}
    			if(mx<query(i,j)){
    				mx=query(i,j);
    				l=i;r=j;
    			}
    			i=j;
    		}
    	}
    	printf("%d
    ",mx);
    	for(int i=l;i<=r;i++){
    		printf("%d ",i);
    	}
    	for(int i=r;i>=l;i--){
    		for(int j=1;j<c[i];j++){
    			printf("%d ",i);
    		}
    	}
    	return 0;
    }
    

    CF1157G Inverse of Rows and Columns

    题目要求求出对于给定的(n imes m)(01)矩阵,是否有选择某些行/列进行反转后使得按照(a_{11},a_{12},cdots,a_{1n},a_{21},cdots,a_{nn})顺序排列后有序的方案,如果有则输出任意可行方案。观察到题目限制条件,易知合法变化后的矩阵应当至多有一行既有(0)也有(1),如果有则在这一行之前的所有行均为(0),之后的所有行均为(1),如果不存在既有(0)也有(1)的行则前一部分行全为(0),后一部分全为(1)。观察到数据范围(n,mleq200),不妨暴力枚举按题目顺序展开后的第一个(1)的位置,然后检验是否有可行的方案即可。

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int N=220;
    
    int n,m;
    
    int opa[N],opb[N];
    
    int a[N][N],b[N][N];
    
    bool can(int u,int v){
    	for(int i=1;i<=n;i++){
    		for(int j=1;j<=m;j++){
    			if(i<u||(i==u&&j<v)){
    				b[i][j]=0;
    			}
    			else{
    				b[i][j]=1;
    			}
    		}
    	}
    	opa[1]=a[1][1]^b[1][1];
    	for(int i=1;i<=m;i++){
    		opb[i]=a[1][i]^b[1][i]^opa[1];
    	}
    	for(int i=2;i<=n;i++){
    		opa[i]=a[i][1]^b[i][1]^opb[1];
    		for(int j=2;j<=m;j++){
    			if(b[i][j]!=(a[i][j]^opa[i]^opb[j])){
    				return 0;
    			}
    		}
    	}
    	return 1;
    }
    
    void print(){
    	printf("YES
    ");
    	for(int i=1;i<=n;i++){
    		putchar('0'+opa[i]);
    	}
    	putchar('
    ');
    	for(int i=1;i<=m;i++){
    		putchar('0'+opb[i]);
    	}
    	putchar('
    ');
    }
    
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++){
    		for(int j=1;j<=m;j++){
    			scanf("%d",&a[i][j]);
    		}
    	}
    	for(int i=1;i<=n;i++){
    		for(int j=1;j<=m;j++){
    			if(can(i,j)){
    				print();
    				return 0;
    			}
    		}
    	}
    	printf("NO
    ");
    	return 0;
    }
    
  • 相关阅读:
    生成二维码
    XML 基础应用
    C# 后台模拟请求一般处理程序
    json
    导出excel td格式设置
    发布IIS后 上传文件过大失败解决方案
    SQL Server 日期+4位流水号
    垮库操作
    触发器结合游标删除关联表数据
    我是到今天才懂得“放疗”是居里夫人发明的,你呢?
  • 原文地址:https://www.cnblogs.com/--BLUESKY007/p/10777478.html
Copyright © 2020-2023  润新知