码迷,mamicode.com
首页 > 其他好文 > 详细

CF Round #631 题解

时间:2020-04-05 15:52:50      阅读:118      评论:0      收藏:0      [点我收藏+]

标签:因此   string   type   set   names   cti   情况   namespace   出现   

\(Codeforces\) \(Round\) \(631\)

A.Dreamoon and Ranking Collection

题目大意:

\(n\)轮比赛,每轮比赛排名已经给出,还可以进行额外的\(m\)场比赛
问:所有比赛进行完后,最多可以收集到从\(1\)开始的多少个连续名次

题解:

用一个数组统计一下排名的出现情况,然后扫一遍添加\(m\)个缺失名次即可

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int main(){
	int i,j,k,n,a,x,t,s[105],ans,mmax;
	scanf("%d",&t);
	while(t--){
		scanf("%d%d",&n,&x);
		memset(s,0,sizeof(s));
		ans=0;mmax=0;
		for(i=1;i<=n;i++){scanf("%d",&a);s[a]=1;mmax=max(mmax,a);}
		for(i=1;i<=mmax;i++){
			if(s[i]){ans=i;continue;}
			else if(s[i]==0&&x){x--;s[i]=1;ans=i;}
			else if(s[i]==0&&x==0){ans=i;break;}
		}
		while(s[ans]==1)ans++;
		ans+=x;
		ans--;
		printf("%d\n",ans);
	}
	return 0;
}

B. Dreamoon Likes Permutations

题目大意:

给出\(n\)和长度为\(n\)的一个数列,可将其分为左右两部分,问有多少种分法,使得左右两部分都为从\(1\)开始的一个排列。输出方案数以及全部方案。

题解:

从左往右和从右往左各扫一次,处理出数组\(l_1\)\(l_2\),分别表示从第\(1\)位和第\(n\)位到第\(i\)位的所有数是否构成一个排列。然后枚举断点,如果左右两侧都是一个排列,那么当前断点就是一个方案,输出即可。

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int t,n,a[200005],mmax,s1[200005],s2[200005],l1[200005],l2[200005];
void doit(){
	int i,j;
	mmax=0;
	for(i=1;i<=n;i++){
		mmax=max(mmax,a[i]);
		s1[a[i]]++;
		if(s1[a[i]]==1&&mmax==i)l1[i]=1;
		else if(s1[a[i]]>=2)break;
	}
	mmax=0;
	for(i=n;i>=1;i--){
		mmax=max(mmax,a[i]);
		s2[a[i]]++;
		if(s2[a[i]]==1&&mmax==n-i+1)l2[i]=1;
		else if(s2[a[i]]>=2)break;
	}
}
int main(){
	int i,j;
	scanf("%d",&t);
	while(t--){
		scanf("%d",&n);
		memset(s1,0,sizeof(s1));
		memset(s2,0,sizeof(s2));
		memset(l1,0,sizeof(l1));
		memset(l2,0,sizeof(l2));
		for(i=1;i<=n;i++){scanf("%d",&a[i]);}
		doit();
		int ans=0;
		for(i=1;i<n;i++)
			if(l1[i]&&l2[i+1])ans++;
		if(ans==0)printf("%d\n",ans);
		else{
			printf("%d\n",ans);
			for(i=1;i<n;i++)
				if(l1[i]&&l2[i+1])
					printf("%d %d\n",i,n-i);
		}
	}
	return 0;
}

C. Dreamoon Likes Coloring

题目大意:

\(n\)个格子,\(m\)次染色次数,每次染色宽度为\(l_i\),颜色为\(i\)
后染的颜色会覆盖原来的颜色
求一种染色方案使得每个格子都有颜色并且最后每个颜色都要出现
若无方案则输出\(-1\)

题解:

1.每个颜色至少要占一个格子,那么在第\(i\)个颜色涂上去之前,必定有\(i-1\)个格子已经有颜色了,因此\(n-li<i-1\)时必定无解。
2.总长度\(<n\)时必定无解。
3.其他情况均为有解情况,为获得方案,只需先默认第i个颜色从第i个格子开始涂。然后再贪心的将部分颜色往后面挪以填充未染色的格子即可。
ps.代码中是从后往前图的颜色

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long lol;
lol n,m,l[100005],sum,ans[100005];
int main(){
	lol i,j;
	scanf("%lld%lld",&n,&m);
	for(i=1;i<=m;i++){scanf("%lld",&l[i]);sum+=l[i];}
	for(i=1;i<=m;i++)
		if(n-l[i]<i-1){
			printf("-1\n");
			return 0;
		}
	if(sum<n){
		printf("-1\n");
		return 0;
	}
	lol len=m+l[m]-1,need=n-len,place=n-l[m]+1;
	for(i=m;i>=1;i--){
		ans[i]=place;
		if(need==0)place--;
		else{ 
			place-=min(l[i-1]-1,need)+1;
			need-=l[i-1]-1;
			if(need<0)need=0;
		}
	}
	for(i=1;i<=m;i++)
		printf("%d ",ans[i]);
	printf("\n");
	return 0;
}

D. Dreamoon Likes Sequences

题目大意:

对于每组数据给出两个整数\(d\)\(m\)
求满足下列条件的数列\(a_n\)的个数对\(m\)取模的结果
1.\(1≤a_1<a_2<?<a_n≤d\)
2.\(a_n\)的前缀异或和\(b_n\)也为单调递增数列

题解:

由异或运算得到本题应从二进制角度来思考。
由异或和单调递增可知,数列\(a_n\)的最高位单调递增。
于是讨论二进制最高位的位数:
1.位数为\(1\)时,有\(1\)
2.位数为\(2\)时,有\(2\)
3.位数为\(3\)时,有\(4\)
...(以此类推)
故答案为\(ans=(1+1)*(2+1)*(4+1)...-1\)
ps.\(+1\)是因为当前数可以不取,\(-1\)是因为要减去全部都不取的情况。

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long lol;
lol n,mod,t;
int main(){
	scanf("%lld",&t);
	while(t--){
		scanf("%lld%lld",&n,&mod);
		lol ans=1%mod,x=1;
		while(x<=n){
			ans=(ans*(min(x,n-x+1)+1))%mod;
			x<<=1;
		}
		ans--;
		ans%=mod;
		while(ans<0)ans+=mod;
		printf("%lld\n",ans);
	}
	return 0;
}

E. Drazil Likes Heap

题目大意:

给定一个层数为\(h\)的满二叉大根堆,再给定一个整数\(g\)
每次操作删除堆中的一个数。
求一个删除堆中元素的方案,使得:
1.最后得到的堆为一个\(g\)层的满二叉大根堆。
2.该堆的元素和最小。
输出最小的和以及删除方案。

题解:

不难得到以下结论:
1.每次删除一个节点,由它大儿子和大孙子组成的链的深度\(-1\)
2.一个节点不可删,当且仅当它大儿子和大孙子等组成的链的深度等于\(g\)
3.一个节点不可删,那么它的大儿子也不可删。由它大儿子和大孙子组成的链都不能删。
于是得到贪心算法,如果根节点能删则删,否则把左右儿子当作根分别进行删除操作。
这样能够保证随后得到的堆的元素和是最小的。

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#define ll(x) (x<<1)
#define rr(x) (x<<1|1)
using namespace std;
typedef long long lol;
int n,m,t,h,g,cnt,a[2500000],ans[2500000];
lol sum;
void del(int x){
	if(a[ll(x)]==0&&a[rr(x)]==0)a[x]=0;
	else{
		if(a[ll(x)]>a[rr(x)]){
			a[x]=a[ll(x)];
			del(ll(x));	
		}
		else{
			a[x]=a[rr(x)];
			del(rr(x));	
		}
	}
}
int get_depth(int x,int depth){
	if(a[x]==0)return depth-1;
	if(a[ll(x)]>a[rr(x)])return get_depth(ll(x),depth+1);
	else return get_depth(rr(x),depth+1);
}
void dfs(int x,int depth){
	if(a[x]==0)return;
	while(get_depth(x,depth)>m){del(x);ans[++cnt]=x;}
	dfs(ll(x),depth+1);
	dfs(rr(x),depth+1);
}
int main(){
	int i;
	scanf("%d",&t);
	while(t--){
		cnt=0;sum=0;
		scanf("%d%d",&n,&m);
		h=(1<<n)-1;g=(1<<m)-1;
		for(i=1;i<=h*2+1;i++)a[i]=0;
		for(i=1;i<=h;i++)scanf("%d",&a[i]);
		dfs(1,1);
		for(i=1;i<=g;i++)sum+=a[i];
		printf("%lld\n",sum);
		for(i=1;i<=cnt;i++)
			printf("%d ",ans[i]);
		printf("\n");
	}
	return 0;
}

BBT

没啥,就是觉得自己菜的一批

CF Round #631 题解

标签:因此   string   type   set   names   cti   情况   namespace   出现   

原文地址:https://www.cnblogs.com/huangdalaofighting/p/12637116.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!