CF676Div2

许久未打的CF比赛,水平不行被自己打成了一场手速赛,20分钟过了A和B之后gg,结果赛后把CDE都能补完了

这种训练难度其实是最好的,感觉自己会的,不一定能1A,不一定能写出来,祝自己省赛顺利


给个A题的链接

A题:数学题,一个n个数字的全排列,所有数字可以任意交换,但是只能交换一次,求最大值n和最小值1的最大的可能距离

要么放到第一个位置,要么放到最后一个位置,枚举即可

int n;
int num[maxn];

int main(){
	//input;
	while(scanf("%d",&n)!=EOF){
		int ans=0;
		for(int i=1;i<=n;i++){
			scanf("%d",&num[i]);
			if (num[i]==n||num[i]==1){
				ans=max(ans,n-i);
				ans=max(ans,i-1);
			}
		}
		cout<<ans<<endl;
	}
	return 0;
}

B题:当作模拟题做吧,有dp思想在里面

题意:第n层有n个杯子,每个杯子的容量为1,第i层的第j个杯子一旦满了之后,会把多余的水分给第i+1层的下面两个杯子,问注T个水量,最终有多少个杯子是满的

定义dp【i】【j】

其中dp【1】【1】=T

那么可以一层一层的模拟

首先假设第i层的杯子容量无穷大,那么可以接收第i-1层的所有水(先存着)

等第i-1层的所有水流完,那么第i层的杯子容量变为1,开始把多余的水平均分给第i+1层的下面两个杯子

由于涉及double,记得eps

代码:

double dp[20][20];
int n,t;

int main(){
	//input;
	while(scanf("%d%d",&n,&t)!=EOF){
		memset(dp,0,sizeof(dp));
		dp[1][1]=t;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=i;j++)
				if (dp[i][j]>=1+eps){
					dp[i+1][j]+=(dp[i][j]-1)/2.0;
					dp[i+1][j+1]+=(dp[i][j]-1)/2.0;
					dp[i][j]=1;
				}
		int ans=0;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=i;j++)
				if (dp[i][j]<1+eps&&dp[i][j]>1-eps)
					ans++;
		cout<<ans<<endl;
	}
	return 0;
}

C题是被教做人的一题,二分思路其实很简单

题意:n个长度的字符串,全是a或者b,最多允许修改k个,求可能出现的最大连续相同字符串的长度

思路:最多为n个,最少为0个是显然的,那么二分的左右界限有了

如果检查长度为x的,是不是可能存在

来个O(n)的扫描就好,首先从串起始开始,取x个字符统计a和b的字符数量

然后区间右移动一个,如果x+1(添加)的地方是a,就加1,如果删除的地方是a,就减1

这样每次判断当前字符数量与需要检查的x的差值,不超过k就可以

代码如下:

int n,k; 
char s[maxn];

bool ok(int x){
	int need=k+1,a=0,b=0;
	for(int i=1;i<=x;i++)
		if (s[i]=='a') a++;
	b=x-a;
	need=min(need,x-a);
	need=min(need,x-b);
	for(int i=x+1;i<=n;i++){
		if (s[i]=='a') a++;
		else b++;
		if (s[i-x]=='a') a--;
		else b--;
		need=min(need,x-a);
		need=min(need,x-b);
	}
	if (need<=k) return true;
	return false;
}

int main(){
	//input;
	while(scanf("%d%d",&n,&k)!=EOF){
		scanf("%s",s+1);
		int L=0,R=n,m,ans;
		while(L<=R){
			m=(L+R)>>1;
			if (ok(m)){
				ans=m;
				L=m+1;
			}
			else
				R=m-1;
		}
		cout<<ans<<endl;
	}
	return 0;
}

PS:附个二分的学习链接

http://www.wonter.net/?p=223


D题:比赛的时候因为不会搞C,所以觉得D能做

题意:给一个n*m的矩阵,里面的字符表示的是可以往哪几个方向走,或者浪费1秒钟,使用功力让所有的符号顺时针旋转90度,即为把门的朝向改了

这个题的难点:字符怎么跟联通简单的匹配出来;怎么旋转;怎么定义状态


其实想到了很简单:求最小值,用bfs,最多旋转4次,定义step【i】【j】【4】,step【】【】【】=-1代表不可达

顺时针修改字符,就直接暴力用if语句提前预处理了

剩下的难点:用字符表示匹配

我的想法是:用接口对接

意思是:如果当前从(x,y)运动到(nx,ny),方向为K,这儿的K表示枚举的上下左右,以上为例

那么(x,y)需要开放从下到上的接口,意思是搞个flag标记

(nx,ny)需要开放从上到下的接口,意思是搞个flag标记

两个标记同时存在,就可以走

所以判断很简单,四个if语句分开四种方向,每个方向里面两个if语句,判断两边对应的接口是不是存在

bfs还有个很经典的坑点,如果起点和终点是同一个点,那么答案是0

int n,m,sx,sy,ex,ey;
char mp[maxn][maxn][4];
int step[maxn][maxn][4];
char s[maxn][maxn];

int dx[]={0,0,0,1,-1};
int dy[]={0,1,-1,0,0};

struct node{
	int x,y,t;
};

bool ok(char c1,char c2,int k){
	int flag1=0,flag2=0; 
	char ch;
	if (k==1){//向右走
		if (c1=='+'||c1=='-'||c1=='>'||c1=='L'||c1=='U'||c1=='D') flag1=1;
		if (c2=='+'||c2=='-'||c2=='<'||c2=='R'||c2=='U'||c2=='D') flag2=1;
	}
	else if (k==2){//向左走
		if (c1=='+'||c1=='-'||c1=='<'||c1=='R'||c1=='U'||c1=='D') flag1=1;
		if (c2=='+'||c2=='-'||c2=='>'||c2=='L'||c2=='U'||c2=='D') flag2=1;
	}
	else if (k==3){//向下走
		if (c1=='+'||c1=='|'||c1=='v'||c1=='L'||c1=='R'||c1=='U') flag1=1;
		if (c2=='+'||c2=='|'||c2=='^'||c2=='L'||c2=='R'||c2=='D') flag2=1;
	}
	else if (k==4){//向上走
		if (c1=='+'||c1=='|'||c1=='^'||c1=='L'||c1=='R'||c1=='D') flag1=1;
		if (c2=='+'||c2=='|'||c2=='v'||c2=='L'||c2=='R'||c2=='U') flag2=1;
	}
	return flag1&&flag2;
}

void DebugMap(){
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
		printf("%c%c",mp[i][j][0],j==m?'\n':' ');		
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
		printf("%c%c",mp[i][j][1],j==m?'\n':' ');
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
		printf("%c%c",mp[i][j][2],j==m?'\n':' ');		
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
		printf("%c%c",mp[i][j][3],j==m?'\n':' ');
}

void DebugStep(){
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
		printf("%d%c",step[i][j][0],j==m?'\n':' ');		
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
		printf("%d%c",step[i][j][1],j==m?'\n':' ');
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
		printf("%d%c",step[i][j][2],j==m?'\n':' ');		
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
		printf("%d%c",step[i][j][3],j==m?'\n':' ');
}

void GetMap(){
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
		mp[i][j][0]=s[i][j];
	for(int k=1;k<=3;k++)
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++){
		if (mp[i][j][k-1]=='-') mp[i][j][k]='|';
		else if (mp[i][j][k-1]=='|') mp[i][j][k]='-';
		else if (mp[i][j][k-1]=='<') mp[i][j][k]='^';
		else if (mp[i][j][k-1]=='v') mp[i][j][k]='<';
		else if (mp[i][j][k-1]=='>') mp[i][j][k]='v';
		else if (mp[i][j][k-1]=='^') mp[i][j][k]='>';
		else if (mp[i][j][k-1]=='D') mp[i][j][k]='L';
		else if (mp[i][j][k-1]=='R') mp[i][j][k]='D';
		else if (mp[i][j][k-1]=='U') mp[i][j][k]='R';
		else if (mp[i][j][k-1]=='L') mp[i][j][k]='U';
		else mp[i][j][k]=mp[i][j][k-1];
	}
	return;
}

int bfs(){
	queue<node> q;
	while(!q.empty()) q.pop();
	node u,v;
	u.x=sx;u.y=sy;u.t=0;
	q.push(u);
	step[u.x][u.y][0]=0;
	while(!q.empty()){
		u=q.front();q.pop();
		if (step[u.x][u.y][(u.t+1)%4]==-1){
			v.x=u.x;v.y=u.y;v.t=(u.t+1)%4;
			step[v.x][v.y][v.t]=step[u.x][u.y][u.t]+1;
			q.push(v);
		}
		for(int k=1;k<=4;k++){
			v.x=u.x+dx[k];
			v.y=u.y+dy[k];
			if (v.x>=1&&v.x<=n&&v.y>=1&&v.y<=m&&step[v.x][v.y][u.t]==-1&&ok(mp[u.x][u.y][u.t],mp[v.x][v.y][u.t],k)){
				if (v.x==ex&&v.y==ey) return step[u.x][u.y][u.t]+1;
				step[v.x][v.y][u.t]=step[u.x][u.y][u.t]+1;
				v.t=u.t;
				q.push(v);
			}
		}
	}
	return -1;
}

int main(){
	//input;
	while(scanf("%d%d",&n,&m)!=EOF){
		memset(step,-1,sizeof(step));
		for(int i=1;i<=n;i++) scanf("%s",s[i]+1);
		scanf("%d%d%d%d",&sx,&sy,&ex,&ey);
		if (sx==ex&&sy==ey) cout<<"0"<<endl;
		else{
			GetMap();
			//DebugMap();
			//DebugStep();
			cout<<bfs()<<endl;	
		}
	}
	return 0;
}

E题:放在最后一题有点不合理,一个数学思路题

比赛的时候都没看题,好多人说很简单

题意:一个最高项n次的多项式,给(a0,a1,……,an)作为系数,问F(X)是不是可以为0

系数有的是给定的,“?”的情况呢,就是两个人轮流填写任意数值,一人一次,博弈


搞法很简单:正常情况,最后一次填数的人赢!为啥,因为可以随便写什么数字啊!缺什么补什么,不就够了

特殊情况1:X=0,意思是F(X)=0,等价于,a0等于0,也就是常数项等于0

常数项谁能够选择到?判断有多少个?就好

特殊情况2:所有数值都给定了,就直接计算值就好了,判断是不是等于0,相当于写了个数学上的多项式模拟

需要用mod素数的方法把值减小,不然太大了担心爆炸

代码如下:

__int64 mod1=1e9+7,mod2=3e8+7,mod3=1e8+9,mod4=5e8+1;
__int64 n,x,a[100009],cnt,i;
char s[100020][20];

int main(){
	//input;
	while(scanf("%I64d%I64d",&n,&x)!=EOF){
		cnt=0;
		for(i=0;i<=n;i++){
			scanf("%s",s[i]);
			if (s[i][0]=='?') cnt++;
		}
		if (x==0){
			if (s[0][0]=='?'){
				if ((n+1-cnt)%2==1) puts("Yes");
				else puts("No");
			}
			else{
				if (s[0][0]=='0') puts("Yes");
				else puts("No");
			}
		}
		else{
			if (cnt==0){
				for(i=0;i<=n;i++){
					a[i]=0;
					bool minus=(s[i][0]=='-');
					__int64 j=minus;
					__int64 len=strlen(s[i]);
					while(j<len) a[i]=a[i]*10+s[i][j++]-'0';
					if (minus) a[i]=-a[i];
				}
				__int64 w1=1,w2=1,w3=1,w4=1,s1=0,s2=0,s3=0,s4=0;
            	for(i=0;i<=n;i++){
                	s1=(s1+w1*a[i])%mod1;
                	s2=(s2+w2*a[i])%mod2;
                	s3=(s3+w3*a[i])%mod3;
                	s4=(s4+w4*a[i])%mod4;
                	w1=w1*x%mod1;
                	w2=w2*x%mod2;
                	w3=w3*x%mod3;
                	w4=w4*x%mod4;
            	}
            	if (s1==0 && s2==0 && s3==0 && s4==0) printf("Yes\n");
                else printf("No\n");
			}
			else printf("%s\n",(n%2==1)?"Yes":"No");
		}
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值