AGC010F Tree Game
题意简述:给定一颗有\(N(N<=3000)\)个节点的树,每个点有一定权值,双方轮流执行:将当前位置权值-1,并将之移动至相邻节点,先无法移动者输;问初始在哪些节点可使先手必胜。
比较有意思的博弈题。
对于任意结点\(r\),我们考虑以它为根。然后我们定义函数\(f(x)=0/1\)表示在以x为根的子树内,是否能够做到先手必胜。那么\(f(x)=1\)的必要条件就是对于\(x\)的子结点,存在一个\(v\),\(f(v)=0\)且\(val_v<val_x\)。因为在以v为根的子树内,无论怎么走都无法使先手获胜,那么以\(x\)为起点的话,先手直接走到\(v\)就可以了,后手就变成了先手。还要避免先后手秦王绕柱走的情况,所以\(val_v<val_x\)。所以若\(r\)作为起始点可行,那么\(f(r)=1\)。
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=3010;
struct fk{int to,nxt;}e[N<<1];
int f[N],n,cnt,head[N],val[N];
void add(int u,int v){e[++cnt].to=v;e[cnt].nxt=head[u];head[u]=cnt;}
void dfs(int u,int fa){
f[u]=0;
for(int i=head[u],v;i;i=e[i].nxt)
if((v=e[i].to)!=fa){
dfs(v,u);
if(!f[v]&&val[u]>val[v])f[u]=1;
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&val[i]);
for(int u,v,i=1;i<n;i++)
scanf("%d%d",&u,&v),add(u,v),add(v,u);
for(int i=1;i<=n;i++){
dfs(i,0);
if(f[i])printf("%d ",i);
}
puts("");
}