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("");
}
原文地址:https://www.cnblogs.com/yxc2003/p/10751450.html