2022牛客多校联赛第六场 题解

比赛传送门
作者: fn


签到题

G题 Icon Design / 图标设计

题目大意
输出图标"NFLS"。
图标

考察内容
模拟

分析
按题意模拟即可。

#include<bits/stdc++.h>
using namespace std;
int n;
int main(){
    cin>>n;
    int m = 13*n+19;
    for(int i = 1;i<=m;i++){
        cout<<"*";
    }
    cout<<endl;
    for(int i = 1;i<=n;i++){
        for(int j = 1;j<=m;j++){
            if(j==1||j==m) cout<<"*";
            else cout<<".";
        }
        cout<<endl;
    }
    //中间字母
    int k = n+3;
    for(int i = 1;i<=2*n+3;i++){
        for(int j = 1;j<=m;j++){
            if(j==1) {cout<<"*";continue;}
            else if(j==n+3) cout<<"@";
            else if(i>=2&&j==k&&j<=3*n+4) {
              cout<<"@"; 
            }
            else if(j==3*n+5) cout<<"@";
            else if((i==1||i==n+2)&&j>=4*n+7&&j<=6*n+9) cout<<"@";
            else if((i!=1&&i!=n+2)&&j==4*n+7) cout<<"@";
            else if(i!=n+2&&i!=2*n+3&&j>4*n+7&&j<6*n+9) cout<<".";
            else if(i!=2*n+3&&j==7*n+11) cout<<"@";
            else if(i==2*n+3&&j>=7*n+11&&j<=9*n+13) cout<<"@";
            else if((i==n+2||i==2*n+3||i==1)&&j>=10*n+15&&j<=12*n+17)cout<<"@";
            else if(i>1&&i<n+2&&j==10*n+15)cout<<"@";
            else if(i<2*n+3&&i>n+2&&j==12*n+17)  cout<<"@";
            else if(j==m) cout<<"*";
            else cout<<".";
        }
        k++;
        cout<<endl;
    }
    
    for(int i = 1;i<=n;i++){
        for(int j = 1;j<=m;j++){
            if(j==1||j==m) cout<<"*";
            else cout<<".";
        }
        cout<<endl;
    }
    for(int i = 1;i<=m;i++){
	    cout<<"*";
	}
	cout<<endl;
}

J题 Number Game / 数字游戏

题目大意
给定 b 1 , b 2 , c b1,b2,c b1,b2,c 和目标值 x x x ,可以进行任意次赋值,使 c = b 1 − c c=b1-c c=b1c 或者 c = b 2 − c c=b2-c c=b2c
求是否可以使 c c c 变为 x x x

考察内容
暴力,找规律

分析
b 1 ! = b 2 b1!=b2 b1!=b2 时,多次交替操作后, c c c 的变化类似等差数列,公差为 a b s ( b − b 2 ) abs(b-b2) abs(bb2) 。对 x x x 取模后和各种可能的结果进行判断即可。

#include<bits/stdc++.h>
#define ll long long
#define cer(x) cerr<<(#x)<<" = "<<(x)<<'\n'
#define endl '\n'
using namespace std;
ll a,b,c,x;
int main(){ 
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	int t; cin>>t;
	while(t--){
		cin>>a>>b>>c>>x;
		ll b2=a-b; // 得到另一个b
		
		if(c==x){
			cout<<"Yes"<<endl;
			continue;
		}
		if(b-c==x || b2-c==x){
			cout<<"Yes"<<endl;
			continue;
		}
		
		if(b!=b2){ // b!=b2时,额外进行一些判断 
			if(x==b2-(b-c) || x==b-(b2-c)){
				cout<<"Yes"<<endl;	
				continue;
			}
		
			ll mod=abs(b-b2);
			ll d=x%mod; // 	
			if(d==c%mod || d==c%mod-mod || d==c%mod+mod){
				cout<<"Yes"<<endl;	
				continue;
			}
			if(d==(b-c)%mod || d==(b-c)%mod-mod || d==(b-c)%mod+mod){
				cout<<"Yes"<<endl;	
				continue;
			}
			if(d==(b2-c)%mod || d==(b2-c)%mod-mod || d==(b2-c)%mod+mod){
				cout<<"Yes"<<endl;	
				continue;
			}
			ll t2=(b2-(b-c))%mod;
			if(d==t2%mod || d==t2%mod-mod || d==t2%mod+mod){
				cout<<"Yes"<<endl;	
				continue;
			}
			t2=(b-(b2-c))%mod;
			if(d==t2%mod || d==t2%mod-mod || d==t2%mod+mod){
				cout<<"Yes"<<endl;	
				continue;
			}
		}
		cout<<"No"<<endl;
	}
	return 0;
}
/*
1
4 2 2 20

1
2 4 3 -9

1
2 4 3 15

*/ 

基本题

B题 Eezie and Pie / 伊兹和馅饼

题目大意

给定一棵有根树,根是结点 1 1 1 ,城市中有 n ( 1 ≤ n ≤ 2 × 1 0 6 ) n (1≤n≤2×10^6) n(1n2×106) 个结点,每个结点上有一个饼屋。每个饼屋将饼往根结点传递(传递的点包括自己),最大传递距离为 d i d_i di

计算每个结点收到多少个饼。

考察内容
树上差分

分析
类似lca预处理一下,树上倍增求出 d i + 1 d_i+1 di+1 辈祖先,然后树上差分。
最后跑一个dfs汇总一下。

#include <bits/stdc++.h>
using namespace std;
#define maxn 2000005
#define ll long long
#define int long long
#define cer(x) cerr<<(#x)<<" = "<<(x)<<'\n'
struct Node{
	int to,nxt;
};
Node edge[maxn<<2]; // 链式前向星要多开几倍数组
int head[maxn<<2],power[maxn],n,m,d[maxn],fa[maxn][30],num;
ll dis[maxn];
ll h[maxn]; // 该节点到根节点的距离

inline int read(){ // 快读
	int s=0;
	char c=getchar();
	while (c<'0' || c>'9') c=getchar();
	while (c>='0' && c<='9') s=s*10+c-'0',c=getchar();
	return s;
}
// 链式前向星
inline void add(int x,int y){
	edge[++num].to=y,edge[num].nxt=head[x],head[x]=num;
}
// 初始化
inline void work(int u,int fath){
	d[u]=d[fath]+1; 
	fa[u][0]=fath;
	for (int i=0;fa[u][i];++i) fa[u][i+1]=fa[fa[u][i]][i];
	
	for (int i=head[u];i;i=edge[i].nxt){
		int e=edge[i].to;
		if (e!=fath) work(e,u);
	}
}

// 累计
inline void Get(int u,int fath){
	for (int i=head[u];i;i=edge[i].nxt){
		int e=edge[i].to;
		if (e==fath) continue;
		Get(e,u);
		power[u]+=power[e];
	}
}

ll getfa(int x,int disx){ // 树上倍增求 x 的 disx 辈祖先  
	if(disx>d[x]-1)return 0; // 返回根节点的祖先
	else if(disx==d[x]-1)return 1; // 返回根节点 
	
	// disx<d[x]-1时 	
	for (int i=22;i>=0;--i){ // 二进制拆分 
		if (disx >= (1<<i)){
			disx -= (1<<i);
			
			x=fa[x][i]; 
		} 
	} 
	return x;
} 

signed main(){ // 树上差分,AC
	n=read();
	int x,y;
	for (int i=1;i<n;++i){
		x=read(); y=read();
		add(x,y); add(y,x);
	}
	work(1,0); // 类似lca进行预处理 
	for(int i=1;i<=n;i++){
		cin>>dis[i];
	}	
	
	for(int i=1;i<=n;i++){
		x=i;
		y=getfa(x,dis[x]+1); // 树上倍增求dis[x]+1辈祖先

		++power[x]; --power[y]; // 树上差分
	}
	
	Get(1,0); // 统计答案 
		
	for(int i=1;i<=n;i++){
		cout<<power[i]<<' ';
	}
	return 0;
}

M题 Z-Game on grid / 网格上的Z-游戏

题目大意
Alice和Bob正在 n × m n×m n×m 的网格上玩游戏,其中每个单元格都有“A”、“B”或“.”写在上面。他们轮流在棋盘上移动棋子,爱丽丝先移动。
最初,棋子位于单元(1,1)上。在每个玩家的回合中,他或她可以将棋子向右或向下移动一个单元格,只要它不超出网格。
在任何时候,如果棋子在带有“A”的单元格中,爱丽丝获胜,游戏结束。如果棋子在带有“B”的单元格中,则鲍勃获胜,游戏结束。如果棋子到达单元格(n,m)而游戏未结束,则为平局。
问Alice是否总能找到赢得比赛、平局或输掉比赛的方法。

考察内容
动态规划

分析

状态:
f [ 1 ] [ i ] [ j ] f[1][i][j] f[1][i][j] 表示考虑 i , j i,j i,j 右下角的格子时的答案 1 1 1 (能不能赢)
f [ 2 ] [ i ] [ j ] f[2][i][j] f[2][i][j] 表示考虑 i , j i,j i,j 右下角的格子时的答案 2 2 2 (能不能平局)
f [ 3 ] [ i ] [ j ] f[3][i][j] f[3][i][j] 表示考虑 i , j i,j i,j 右下角的格子时的答案 3 3 3 (能不能输)

边界:
右下角

if(s[n-1][m-1]=='A'){
	f[1][n-1][m-1]=1; // 可以赢 
}
else if(s[n-1][m-1]=='.'){
	f[2][n-1][m-1]=1; // 可以平局 
}
else{ // 'B'
	f[3][n-1][m-1]=1; // 可以输 
}

转移:
从右下角倒着转移回左上角。轮到Alice移动时取max,轮到Bob移动时取min。

完整代码:

#include<bits/stdc++.h>
#define ll long long
#define cer(x) cerr<<(#x)<<" = "<<(x)<<'\n'
#define endl '\n'
using namespace std;
const int N=505;
ll n,m;
string s[N];
int f[4][N][N]; // f[1][i][j]表示考虑i,j右下角时的答案1
 
int main(){ 
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	int t; cin>>t;
	while(t--){
		cin>>n>>m;
		for(int i=0;i<n;i++){
			cin>>s[i];
		}
		for(int i=1;i<=3;i++){
			for(int j=0;j<=n;j++){
				for(int k=0;k<=m;k++){
					f[i][j][k]=0; // 初始化 
				}
			}
		}
		
		if(s[n-1][m-1]=='A'){
			f[1][n-1][m-1]=1; // 可以赢 
		}
		else if(s[n-1][m-1]=='.'){
			f[2][n-1][m-1]=1; // 可以平局 
		}
		else{ // 'B'
			f[3][n-1][m-1]=1; // 可以输 
		}
		
		// 转移f[1][j][k],是否能赢 
		for(int j=n-1;j>=0;j--){
			for(int k=m-1;k>=0;k--){
				if(j==n-1 && k==m-1)continue;
				 
				if(s[j][k]=='A'){
					f[1][j][k]=1; 
				}
				else if(s[j][k]=='B'){
					f[1][j][k]=0; 
				}
				else{ // '.'
					if((j+k)%2==0){ // 轮到Alice走 
						if(j+1<=n-1)f[1][j][k]=max(f[1][j][k],f[1][j+1][k]);
						if(k+1<=m-1)f[1][j][k]=max(f[1][j][k],f[1][j][k+1]);
					}
					else{ // 轮到Bob走
						f[1][j][k]=1;
						if(j+1<=n-1)f[1][j][k]=min(f[1][j][k],f[1][j+1][k]);
						if(k+1<=m-1)f[1][j][k]=min(f[1][j][k],f[1][j][k+1]);
					}
				}
			}
		}
		
		// 转移f[3][j][k],是否能输 
		for(int j=n-1;j>=0;j--){
			for(int k=m-1;k>=0;k--){
				if(j==n-1 && k==m-1)continue;
				
				if(s[j][k]=='A'){
					f[3][j][k]=0; 
				}
				else if(s[j][k]=='B'){
					f[3][j][k]=1; 
				}
				else{ // '.'
					if((j+k)%2==0){ // 轮到Alice走 
						if(j+1<=n-1)f[3][j][k]=max(f[3][j][k],f[3][j+1][k]);
						if(k+1<=m-1)f[3][j][k]=max(f[3][j][k],f[3][j][k+1]);
					}
					else{ // 轮到Bob走
						f[3][j][k]=1;
						if(j+1<=n-1)f[3][j][k]=min(f[3][j][k],f[3][j+1][k]);
						if(k+1<=m-1)f[3][j][k]=min(f[3][j][k],f[3][j][k+1]);
					}
				}
			}
		}
		
		// 转移f[2][j][k],是否能平局 
		for(int j=n-1;j>=0;j--){
			for(int k=m-1;k>=0;k--){
				if(j==n-1 && k==m-1)continue;
				
				if(s[j][k]=='A'){
					f[2][j][k]=0; 
				}
				else if(s[j][k]=='B'){
					f[2][j][k]=0; 
				}
				else{ // '.'
					if((j+k)%2==0){ // 轮到Alice走
						if(j+1<=n-1)f[2][j][k]=max(f[2][j][k],f[2][j+1][k]);
						if(k+1<=m-1)f[2][j][k]=max(f[2][j][k],f[2][j][k+1]);
					}
					else{ // 轮到Bob走
						f[2][j][k]=1;
						if(j+1<=n-1)f[2][j][k]=min(f[2][j][k],f[2][j+1][k]);
						if(k+1<=m-1)f[2][j][k]=min(f[2][j][k],f[2][j][k+1]);
					}
				}
			}
		}
		
		if(f[1][0][0])cout<<"yes "; // 可以赢 
		else cout<<"no ";
		if(f[2][0][0])cout<<"yes "; // 可以平局 
		else cout<<"no ";
		if(f[3][0][0])cout<<"yes"<<endl; // 可以输 
		else cout<<"no"<<endl;
	}
	return 0;
}
/*
1
1 4
...B

1
3 3
..B
..B
BB.

1
3 4
.A..
B...
....

*/ 

进阶题

A题 Array / 数组

题目大意
给定一个数组 a 1 , . . . , a n a_1,...,a_n a1,...,an ,满足 ∑ 1 a i ≤ 1 2 \displaystyle\sum\dfrac 1{a_i} \leq\dfrac1 2 ai121

找出 m m m 个整数 c 0 , . . . , c i c_0,...,c_i c0,...,ci 来构造一个无限序列 b b b ,其中 b i = c i   m o d   m b_i=c_{i\bmod m} bi=cimodm b b b 中每隔 a i a_ i ai 个数至少出现一次 i i i

考察内容
数学知识,排序,暴力,哈夫曼树

分析

解法一:
把每个 𝑎 𝑖 𝑎_𝑖 ai 改成小于他的最大的 2 2 2 的幂(high_bit),因为原先倒数和小于等于 1 2 \dfrac1 2 21,所以修改之后倒数和小于等于 1 1 1 。之后按照哈夫曼树合并。

解法二:
按照 a a a 从小到大排序,然后每隔 a [ i ] a[i] a[i] 个直接填下标。填完剩下的放1。

#include<bits/stdc++.h>
using namespace std;
int n,ans[1000005],m;
pair<int,int>a[100005];
int main(){
	// 读入 
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i].first);
		a[i].second=i; // 记录下标 
	}
	sort(a+1,a+1+n); // 从小到大排序 
	
	// 填数字 
	m=1e6; // 足够大 
	for(int i=1,b=1;i<=n;b++,i++){
		while(ans[b]!=0)b++; // 找到第一个没填的位置 
		
		for(int j=b;;j+=a[i].first){ // 每隔a[i]个填一次 
			while(j>m || ans[j]!=0)j--; // 保证不越界且没填 
			
			ans[j]=a[i].second; // 填上下标 
			if(m-j+b<=a[i].first)break; 
		}
	}
	
	// 输出答案 
	printf("%d\n",m);
	for(int i=1;i<m;i++){
		printf("%d ",ans[i]?ans[i]:1);
	}
	printf("%d\n",ans[m]?ans[m]:1);
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值