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("");
}
posted @ 2019-04-22 17:32  努力进步的肥宅yxc  阅读(210)  评论(0编辑  收藏  举报