题意简述:给定一颗有(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("");
}