AtCoder Grand Contest 010

直接开始讲题吧。
比赛链接在这:阿巴

A - Addition

只需要看看奇数的个数是否为奇数即可。

#include<bits/stdc++.h>
using namespace std;

const int N=100010;
int a[N],n,t[2];

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]),t[a[i]&1]++;
	printf(t[1]&1?"NO\n":"YES\n");
}

B - Boxes

想了一会
首先考虑差分,操作就变成给全局 + 1 +1 +1之后再给某个位置 − n -n n
将差分数组 b b b处理出来,观察全局要加多少个 1 1 1
显然如果 b i b_i bi是正的那么就要加至少 b i b_i bi个1,否则就加到与 b i b_i bi同余的那个数就可以了。
记这个全局加的次数为 m m a x mmax mmax,试着加一下,若出现 m o d    n ≠ 0 \mod n\not=0 modn=0的情况,那么非法。可以处理出来每一个点对应的操作次数,操作次数总和是 t o t tot tot,若不相等,那么无论怎么操作这两个东西都不会相等,非法。
最后还要看看这样加完之后序列在大小上是否合法,只需要考虑其中一位的取值就可以了。

#include<bits/stdc++.h>
using namespace std;

const int N=100010;
int n,a[N];

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	int tmp=a[n],mmax=0;
	for(int i=n;i>=1;i--) a[i]-=a[i-1];a[1]-=tmp;
	for(int i=1;i<=n;i++){
		a[i]=-a[i];
		if(a[i]<0) mmax=max(mmax,-a[i]);
		else mmax=max(mmax,(a[i]%n==0?0:n-a[i]%n));
	}
	long long tot=0,ans=0;
	bool tf=true;
	for(int i=1;i<=n;i++) {
		a[i]+=mmax;
		if(a[i]%n) {tf=false;break;}
		tot+=a[i]/n;
		ans+=1ll*a[i]/n*((n-i)%n+1);
	}
	if(tot!=mmax || tmp<ans || ans<=tmp && (tmp-ans)%(1ll*(1+n)*n/2)) tf=false;
	printf(tf?"YES\n":"NO\n");
}

C - Cleaning

考虑 d f s dfs dfs,每一个子树会返回上来一下需要解决的路径数。
然后根据当前节点的 a [ x ] a[x] a[x]可以解出来当前节点返回的路径数量,剩下的两两配对。
若配对数为 d d d,那么两两配对要满足不存在一个子树的路径数 > d >d >d,多出来的直接算到返回的中。
中途若有不合法的情况直接输出非法即可。
根节点不能有返回值。

#include<bits/stdc++.h>
using namespace std;

const int N=100010;
struct edge{
	int y,nex;
}s[N<<1];
int n,first[N],len,a[N],in[N];
bool tf=false;

void ins(int x,int y){
	s[++len]=(edge){y,first[x]};first[x]=len;
}

int dfs(int x,int fa){
	if(in[x]==1) return a[x];
	vector<int> V;
	long long tot=0;
	for(int i=first[x];i!=0;i=s[i].nex) if(s[i].y!=fa){
		int tmp=dfs(s[i].y,x);
		tot+=tmp;
		V.push_back(tmp);
	}
	if(tot>2*a[x]) {printf("NO\n");exit(0);}
	tot=2*a[x]-tot;
	int d=a[x]-tot;
	if(d<0) {printf("NO\n");exit(0);}
	long long ans=0;
	for(int i=0;i<V.size();i++) if(V[i]>d) ans+=V[i]-d;
	if(ans>tot) {printf("NO\n");exit(0);}
	return tot;
}

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	int x,y;
	for(int i=1;i<n;i++) {
		scanf("%d %d",&x,&y);
		in[x]++;in[y]++;
		ins(x,y),ins(y,x);
	}
	if(n==1) printf("NO\n");
	else if(n==2) printf(a[1]==a[2]?"YES\n":"NO\n");
	else{
		int rt=0;
		for(int i=1;i<=n;i++) if(in[i]>1) {rt=i;break;}
		int d=dfs(rt,0);
		printf(d?"NO\n":"YES\n");
	}
}

D - Decrementing

直接从 g c d gcd gcd入手好像不太行,考虑奇偶性。
如果存在奇数个偶数,那么先手必胜:
首先发现当前的局面至少存在一个奇数,否则 g c d gcd gcd不为 1 1 1
先将其中一个偶数操作为奇数,操作之后的 g c d gcd gcd也不可能为偶数,不会改变奇偶性,此时局面至少存在两个奇数。
若后手将其中一个奇数 − 1 -1 1变成偶数,由于至少存在两个奇数,所以 g c d gcd gcd也不可能为偶数,不会改变奇偶性,先手只需将这个偶数 − 1 -1 1即可。
若后手将其中一个偶数 − 1 -1 1,则先手将另一个偶数 − 1 -1 1
最终后手剩下 0 0 0个偶数,且奇数全为 1 1 1, 输掉了游戏。
若存在偶数个偶数,而且奇数个数 > 1 >1 >1,那么先手必败,正确性同上。
只剩下一种情况:
存在偶数个偶数并且奇数个数 = 1 =1 =1
显然不可能操作偶数,否则后手可以操作另一个偶数,必败。
如果这个奇数不为 1 1 1,那么操作它,观察下一个局面是否必败或者必胜即可。
否则必败。

#include<bits/stdc++.h>
using namespace std;

const int N=100010;
int a[N],n;

int gcd(int x,int y){
	return y==0?x:gcd(y,x%y);
}

bool gs(){
	int tot=0;
	for(int i=1;i<=n;i++) tot+=(a[i]&1);
	if((n-tot)&1) return true;
	if(tot>1) return false;
	tot=0;
	for(int i=1;i<=n;i++) if(a[i]==1) return false;
	for(int i=1;i<=n;i++) a[i]-=(a[i]&1),tot=gcd(tot,a[i]);
	for(int i=1;i<=n;i++) a[i]/=tot;
	return gs()^true;
}

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	printf(gs()?"First":"Second");
}

E - Rearranging

首先很显然的一个性质:两个不互质的数的相对关系不会改变。
可以联想到给不互质的数连一条无向边,先手给其定向,后手要使得拓扑序最大。
对于每一个联通块而言,肯定使得最小点为第一个点,后面考虑 d f s dfs dfs
贪心地从小到大遍历有连边而且没有遍历过的点,并给这条边定向。
合并的时候按照拓扑序最大来合并就可以了。
为了方便操作,博主倒序存储了这个列表,方便在最前面插入一个点。

#include<bits/stdc++.h>
using namespace std;

const int N=2010;
int a[N],n,tmp[N];
vector<int> ans;
bool d[N][N];
bool vis[N],we[N];

int gcd(int x,int y){return y==0?x:gcd(y,x%y);}

void merge(vector<int>&A,vector<int> B){
	tmp[0]=0;
	int a=A.size()-1,b=B.size()-1;
	while(a>=0 && b>=0){
		if(A[a]>B[b]) tmp[++tmp[0]]=A[a--];
		else tmp[++tmp[0]]=B[b--];
	}
	while(a>=0) tmp[++tmp[0]]=A[a--];
	while(b>=0) tmp[++tmp[0]]=B[b--];
	A.resize(tmp[0]);
	for(int i=tmp[0]-1;i>=0;i--) A[i]=tmp[tmp[0]-i];
}

vector<int> gs(int x){
	vis[x]=true;
	vector<int> A;
	for(int i=1;i<=n;i++) if(!vis[i] && d[x][i])
		merge(A,gs(i));
	A.push_back(x);
	return A;
}

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	sort(a+1,a+1+n);
	for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) d[i][j]=(gcd(a[i],a[j])!=1);
	for(int i=1;i<=n;i++) if(!vis[i]) merge(ans,gs(i));
	for(int i=n-1;i>=0;i--) printf("%d ",a[ans[i]]);
}

F - Tree Game

从当前点开始 d f s dfs dfs
肯定不会走 a [ y ] > = a [ x ] a[y]>=a[x] a[y]>=a[x]的点,否则来回横跳就可以把自己消耗完。
那么肯定是走 a [ y ] < a [ x ] a[y]<a[x] a[y]<a[x]的点,观察是否后手必败即可。

#include<bits/stdc++.h>
using namespace std;

const int N=3010;
struct edge{
	int y,nex;
}s[N<<1];
int first[N],len;
int n,a[N];

void ins(int x,int y){
	s[++len]=(edge){y,first[x]};first[x]=len;
}

bool dfs(int x){
	for(int i=first[x];i!=0;i=s[i].nex) if(a[s[i].y]<a[x] && !dfs(s[i].y))
		return true;
	return false;
}

int main(){
	scanf("%d",&n);
	int x,y;
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1;i<n;i++) scanf("%d %d",&x,&y),ins(x,y),ins(y,x);
	for(int i=1;i<=n;i++) if(dfs(i)) printf("%d ",i);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值