AtCoder Grand Contest 010
AtCoder Grand Contest 010
A - Addition
翻译
黑板上写了\(n\)个正整数,每次会擦去两个奇偶性相同的数,然后把他们的和写会到黑板上,问最终能否只剩下一个数。
题解
洛谷认为此题过水,已被隐藏。
#include<iostream>
#include<cstdio>
using namespace std;
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int main()
{
int n=read(),s=0;while(n--)s+=read()&1;
puts((s&1)?"NO":"YES");return 0;
}
B - Boxes
翻译
有\(n\)个盒子排成了一圈,第\(i\)个盒子里有\(a_i\)个石头,每次可以选择一个盒子,假定它是\(i\)号,那么对于\(j=[1,n]\),第\((i+j)\)号盒子会被丢掉\(j\)个石头,特别的,第\(i+n\)个盒子就是第\(i\)个盒子。问最终能否让所有盒子都变为空盒子。
题解
我表示不会做。
首先通过所有元素之和可以很容易计算出总共减了多少次。现在考虑相邻两项之间的差,设总共减了\(S\)次,在\(i\)位置减了\(x\)次。那么可以知道\(a_i-a_{i+1}=x*(n-1)-S\)。这样子可以很容易的解出所有的\(x\)来,最后再\(check\)一下是否合法即可。
#include<iostream>
#include<cstdio>
using namespace std;
#define MAX 100100
#define ll long long
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int n,a[MAX];
ll v[MAX],S,s,tot;
int main()
{
n=read();s=1ll*n*(n+1)/2;
for(int i=1;i<=n;++i)S+=(a[i]=read());a[n+1]=a[1];
if(n==1){puts("YES");return 0;}
if(S%s){puts("NO");return 0;}
S/=s;
for(int i=1;i<=n;++i)
{
v[i]=a[i]-a[i+1]+S;
if(v[i]%n||v[i]<0){puts("NO");return 0;}
tot+=v[i]/n;
}
if(tot!=S)puts("NO");else puts("YES");
return 0;
}
C - Cleaning
翻译
给定一棵树,每个点上有若干个石头,每次可以选择两个叶子节点,将路径上所有的点全部丢掉一块石头,问能否丢掉所有石头。
题解
考虑从叶子开始向上依次满足。如果一个点上有\(a_i\)个石子,那么它的叶子节点中的路径要达到这个点以及它的父亲总共\(a_i\)次。剩下的显然不能够出子树,只能够自己在子树内部匹配,显然子树内部每匹配一对点,那么它出去路径的总数就会减少\(2\)。对于每一个点,首先计算它的子树中最多能够出去的点数,然后计算一下在当前点的最大匹配数,如果当前所有点都出去了还满足不了当前点显然无解,如果当前点能够匹配的全部匹配上了还比当前点多显然也无解,否则恰好当前点需要的数量的路径出去就好了。
我怎么感觉乱搞,搞着搞着就过了呢???
#include<iostream>
#include<cstdio>
using namespace std;
#define MAX 100100
#define ll long long
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
void WA(){puts("NO");exit(0);}
struct Line{int v,next;}e[MAX<<1];
int h[MAX],cnt=1,dg[MAX];
inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;++dg[u];}
int n,a[MAX],rt;
void dfs(int u,int ff)
{
int mx=0;ll sum=0;
for(int i=h[u];i;i=e[i].next)
{
int v=e[i].v;if(v==ff)continue;
dfs(v,u);mx=max(mx,a[v]);sum+=a[v];
}
if(dg[u]==1)return;
ll mn=min(sum/2,sum-mx);
if(a[u]>sum)WA();
else
{
if(sum-mn>a[u])WA();
else a[u]-=sum-a[u];
}
}
int main()
{
n=read();
for(int i=1;i<=n;++i)a[i]=read();
if(n==2){puts(a[1]==a[2]?"YES":"NO");return 0;}
for(int i=1;i<n;++i)
{
int u=read(),v=read();
Add(u,v);Add(v,u);
}
for(int i=1;i<=n;++i)
if(dg[i]>1){dfs(rt=i,0);break;}
if(a[rt])WA();else puts("YES");
sreturn 0;
}
D - Decrementing
翻译
题解
先考虑简单情况,如果不考虑除去\(gcd\),那么胜负情况之和奇偶性相关。现在加上\(gcd\),如果\(gcd\)是一个奇数显然对于答案也没有影响。那么唯一对于答案有影响的只有\(gcd\)是一个偶数。显然如果有奇数个偶数的话,显然一定可以控制局面保证永远让自己赢。否则的话考虑先手能够改变一个数,使得自己能赢,然后我们并不知道是否可行,但是我们知道先手一定要去改这个数,所以递归算一下即可。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAX 100100
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int n,s,a[MAX];
bool calc(int p)
{
int s=0,mn=1e9;
for(int i=1;i<=n;++i)s+=a[i]&1,mn=min(mn,a[i]);
if((n-s)&1)return p;
if(s>1||mn==1)return p^1;
for(int i=1;i<=n;++i)a[i]>>=1;
int d=a[1];
for(int i=1;i<=n;++i)d=__gcd(d,a[i]);
for(int i=1;i<=n;++i)a[i]/=d;
calc(p^1);
}
int main()
{
n=read();for(int i=1;i<=n;++i)a[i]=read();
puts(calc(1)?"First":"Second");
return 0;
}
E - Rearranging
翻译
给定一个数列,先手可以任意排列,后手每次可以交换相邻的两个互质的数,先手会让字典序最小,后手会让字典序最大,求最终排列。
题解
先考虑给定一个排列,后手如何让字典序最大。每个数向后面的所有和它不互质的数连边,求一遍拓扑序即可。那么我们现在来考虑如何让先手构造这个排列,首先我们把不互质的数之间连双向边,构成一个图,对于每一个联通块而言,我们显然要拉一条字典序最小的链出来,直接按顺序\(dfs\)一遍就好了。这样子给边定向跑拓扑排序即可。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define MAX 2020
inline ll read()
{
ll x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
struct Line{int v,next;}e[MAX*MAX];
int h[MAX],cnt=1,dg[MAX];
vector<int> E[MAX];
inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;dg[v]+=1;}
inline void ADD(int u,int v){E[u].push_back(v);}
int n;
ll a[MAX];
priority_queue<int> Q;
bool vis[MAX];
void dfs(int u)
{
vis[u]=true;
for(int v:E[u])if(!vis[v])dfs(v),Add(u,v);
}
int main()
{
n=read();
for(int i=1;i<=n;++i)a[i]=read();
sort(&a[1],&a[n+1]);
for(int i=1;i<=n;++i)
for(int j=i+1;j<=n;++j)
if(__gcd(a[i],a[j])>1)ADD(i,j),ADD(j,i);
for(int i=1;i<=n;++i)if(!vis[i])dfs(i);
for(int i=1;i<=n;++i)if(!dg[i])Q.push(i);
while(!Q.empty())
{
int u=Q.top();Q.pop();printf("%lld ",a[u]);
for(int i=h[u];i;i=e[i].next)
if(!--dg[e[i].v])Q.push(e[i].v);
}
return 0;
}
F - Tree Game
翻译
题解
这场怕是博弈/构造疯了,我感觉后面几道题都长得一样的。
判定方法是,如果一个子树必胜,那么必定存在一个它的儿子,满足儿子的权值小于当前点的权值,并且儿子是一个必败节点。这个很显然,因为如果这样一个儿子存在的话,你直接给他移过去,如果对面不服再移回来,你就再给他移过去,移到他服为止,当然了,他也只能服。否则的话不是必胜点,那么是一个必败点咯。
#include<iostream>
#include<cstdio>
using namespace std;
#define ll long long
#define MAX 3030
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int n,a[MAX],sg[MAX];
struct Line{int v,next;}e[MAX<<1];
int h[MAX],cnt=1;
inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;}
void dfs(int u,int ff)
{
sg[u]=0;
for(int i=h[u];i;i=e[i].next)
if(e[i].v!=ff&&a[e[i].v]<a[u]){dfs(e[i].v,u);if(!sg[e[i].v])sg[u]=1;}
}
int main()
{
n=read();
for(int i=1;i<=n;++i)a[i]=read();
for(int i=1,u,v;i<n;++i)u=read(),v=read(),Add(u,v),Add(v,u);
for(int u=1;u<=n;++u){dfs(u,0);if(sg[u])printf("%d ",u);}
puts("");return 0;
}