“蔚来杯“2022牛客暑期多校训练营6

J. Number Game

“蔚来杯“2022牛客暑期多校训练营6_第1张图片

给出A,B,C三个数,每次操作可以把B变为A-B,也可以将C变成B-C,给出x,问经过若干次操作之后能否使得C等于x。

思路:首先有一个性质可以知道,这个x一定是两种操作交替进行得来的因为如果一个操作连续做两次,相当于没有操作。这样我们可以倒着推:

在这里我们规定B'=A-B:

根据这些推导,可以知道此时满足条件:(C+x-B)%(B-B')==0,而因为第一步是先改变B的值还是先改变C的值不确定,所以另一种的满足条件即为:(C+x-B')%(B-B')==0。注意特判:当C==x时,不需要进行任何操作;如果A=2*B,即改变B的操作没有任何作用,此时只有B-C==x时才会满足条件。

AC Code:

#include 

int t,A,B,C,x;

int main(){
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);
	std::cout.tie(0);
	std::cin>>t;
	while(t--){
		std::cin>>A>>B>>C>>x;
		int BB=A-B;
		if(C==x){
			std::cout<<"Yes"<<'\n';
			continue;
		}
		if(2*B==A&&B-C==x){
			std::cout<<"Yes"<<'\n';
			continue;
		}
		if(2*B==A){
			std::cout<<"No"<<'\n';
			continue;
		}
		if((C-x)%(BB-B)==0){
			std::cout<<"Yes"<<'\n';
			continue;
		}
		if((C+x-B)%(B-BB)==0||(C+x-BB)%(B-BB)==0){
			std::cout<<"Yes"<<'\n';
			continue;
		}
		std::cout<<"No"<<'\n';
	}
	return 0;
}

os:签到好难。。

G. Icon Design

“蔚来杯“2022牛客暑期多校训练营6_第2张图片

“蔚来杯“2022牛客暑期多校训练营6_第3张图片给出数字n,输出等比例的图像。

思路:(1)模拟,队友搞的,很强;(2)数据范围小于等于5,直接打表也很不错。

AC Code:

#include

int n;

void N(int n,int h){
	if(h==1||h==2*n+3){
		for(int i=1;i<=2*n+3;i++){
			if(i==1||i==2*n+3) std::cout<<"@";
			else std::cout<<".";
		}
	}
	else{
		for(int i=1;i<=2*n+3;i++){
			if(i==1||i==2*n+3||i==h) std::cout<<"@";
			else std::cout<<".";
		}
	}
}
void F(int n,int h){
	if(h==1||h==n+2){
		for(int i=1;i<=2*n+3;i++)
			std::cout<<"@";
	}
	else{
		std::cout<<"@";
		for(int i=1;i<=2*n+2;i++)
			std::cout<<".";
	}
}
void L(int n,int h){
	if(h==2*n+3){
		for(int i=1;i<=2*n+3;i++)
			std::cout<<"@";
	}
	else{
		std::cout<<"@";
		for(int i=1;i<=2*n+2;i++)
			std::cout<<".";
	}
}

void S(int n,int h){
	if(h==1||h==2*n+3||h==n+2){
		for(int i=1;i<=2*n+3;i++)
			std::cout<<"@";
	}
	if(h>1&&hn+2&&h<2*n+3){
		for(int i=1;i<=2*n+2;i++)
			std::cout<<".";
		std::cout<<"@";
	}
}

int main(){
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);
	std::cout.tie(0);
	std::cin>>n;
	for(int i=1;i<=13*n+19;i++)
		std::cout<<"*";
	std::cout<<'\n';
	for(int i=1;i<=n;i++)
		for(int j=1;j<=13*n+19;j++){
			if(j==1) std::cout<<"*";
			else if(j==13*n+19) std::cout<<"*"<<'\n';
			else std::cout<<".";
		}
	for(int i=1;i<=2*n+3;i++)
	{
		for(int j=1;j<=13*n+19;j++)
		{
			if(j==1) std::cout<<"*";
			else if(j==13*n+19) std::cout<<"*"<<'\n';
			else if(j==n+3)
			{
				N(n,i);
				for(int k=1;k<=n+1;k++)
					std::cout<<".";
				F(n,i);
				for(int k=1;k<=n+1;k++)
					std::cout<<".";
				L(n,i);
				for(int k=1;k<=n+1;k++)
					std::cout<<".";
				S(n,i);
				for(int k=1;k<=n+1;k++)
					std::cout<<".";
				j=13*n+18;
			}
			else std::cout<<".";
		}
	}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=13*n+19;j++){
			if(j==1) std::cout<<"*";
			else if(j==13*n+19) std::cout<<"*"<<'\n';
			else std::cout<<".";
		}
	for(int i=1;i<=13*n+19;i++)
		std::cout<<"*";
	std::cout<<'\n';
	return 0;
} 

 B. Eezie and Pie

“蔚来杯“2022牛客暑期多校训练营6_第4张图片

 给定一棵树,每个点给出一个权值d[i],表示该位置向1走的这条路径中最多走d[i]步,问每个点可以由几个点到达。

思路:每个点可以向上走d[i]步,说明这一段每个点的结果都要+1,很显然是个差分问题,将O(n)转化为O(1)修改,然后最后一步DFS统计答案即可,基本是个树上点差分的板子题。

AC Code:

#include 

#define int long long
typedef long long ll;
const int N=2e6+5;
int n,cnt;
int head[N],e[N<<1],next[N<<1],id[N],out[N];
int deep[N],size[N],top[N],fa[N],son[N];
int d[N],fat[N][30],f[N],w[N];

void add_edge(int u,int v){
	e[cnt]=v;
	next[cnt]=head[u];
	head[u]=cnt++;
}

void DFS1(int u,int father){
	deep[u]=deep[father]+1;
	size[u]=1;
	fa[u]=father;
	for(int k=1;k<=25;k++){
		fat[u][k]=fat[fat[u][k-1]][k-1];
	}
	for(int i=head[u];~i;i=next[i]){
		int j=e[i];
		if(j==father) continue;
		fat[j][0]=u;
		DFS1(j,u);
		size[u]+=size[j];
		if(size[son[u]]=0;k--){
		if(deep[fat[u][k]]>=depth)
			u=fat[u][k];
	}
	return u;
}

signed main(){
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);
	std::cout.tie(0);
	std::cin>>n;
	memset(head,-1,sizeof(head));
	for(int i=1;i<=n-1;i++){
		int u,v;
		std::cin>>u>>v;
		add_edge(u,v);
		add_edge(v,u);
	}
	DFS1(1,0);
	std::vectord(n+5,0);
	for(int i=1;i<=n;i++){
		std::cin>>d[i];
	}
	for(int i=1;i<=n;i++){
		int tar=deep[i]-d[i];
		tar=std::max((int)1,tar);
		int t=LCA(i,tar);
		w[fa[t]]--;
		w[i]++;
	}
	DFS2(1,0);
	for(int i=1;i<=n;i++){
		std::cout<

os:一开始队友写的暴力,很容易就T了一发,,不打这种比较正式难度的比赛不知道,一打发现原来签到的知识点都没学全呜呜呜

Z-Game on Grid

“蔚来杯“2022牛客暑期多校训练营6_第5张图片

给出一个n*m的棋盘,由'.','a','b'组成,走到a算Alice赢,走到b算Bob赢,走到(n,m)平局,每次每个人可以向右或者向下走一格,Alice先手,问是否有先手必胜,平局或者必输的策略。

思路:考虑DP。我也不知道为什么,感觉很怪。标解给的是记忆化搜索的做法。即倒着进行DP转移,我们在计算先手必胜时,设定A处值为1,B处为0,(n,m)处为0,根据到某一步是先手还是后手分类讨论,当到达(i,j)点且为先手操作时,只要(i+1,j)或者(i,.j+1)任意一个是先手必胜态则一定会胜;若当前为后手操作时,仅当(i,j+1)和(i+1,j)两点都为先手必胜态才会胜,即表示为符号:

其他两种情况类似考虑,只是设置不同的初始值即可。

题解写的很妙,它把状态表示成二进制,即题解中1,2,4分别表示001,010,100三种状态,这样类似状态压缩的方法可以学习。 

AC Code:

#include 

typedef long long ll;
const int N=505;
int t,n,m;
char f[N][N][2];
std::string s[N];

int DFS(int x,int y,int u){
	if(s[x][y]=='A') return 1;
	if(s[x][y]=='B') return 4;
	if(x==n-1&&y==m-1) return f[x][y][u]=2;
	char &q=f[x][y][u];
	if(q==-1){
		if(u==0){
			q=0;
			if(x+1>t;
	while(t--){
		std::cin>>n>>m;
		for(int i=0;i>s[i];
		}
		memset(f,-1,sizeof(f));
		int ans=DFS(0,0,0);
		std::cout<<(ans&1?"yes":"no")<<' '<<(ans&2?"yes":"no")<<' '<<(ans&4?"yes":"no")<<'\n';
	}
	return 0;
}

补不动了,就这样吧

你可能感兴趣的:(补题专栏,c++,算法)