您的位置:首页 > 其它

AtCoder Grand Contest 010 F - Tree Game 博弈论

2017-11-03 14:45 549 查看

题意

这里有一个N 个点的树, 节点从1 到N 编号, 第i 条边连接了ai 和bi.

一开始第i 个点上有Ai 个石头. Takahashi 和Aoki 会玩一个游戏.

首先, Takahashi 会选择一个出发点; 然后, 从Takahashi 开始, 他们会轮流进行如下操作:

• 首先, 从当前的点上拿走一个石头.

• 然后, 走到一个相邻的点上.

当一个玩家无法执行操作时, 他就输了. 请你找到所有可以让Takahashi 获胜的出发点.

1 <= N <= 10^6, 1 <= ai, bi <= N; 0 <= Ai <= 10^9

分析

不难分析到如果某一时刻先手把某个格子走成0,则先手必败。

接着想,如果某个点周围所有的点都不小于它,那么先手必败。

反之,假设周围的点有小于它的,但如果我选择走不小于它的点,我也必败。

所以,先手一定会往比它小的点走,直到走到某一时刻周围的点都比它大为止。

那么我们就得到了一个朴素的算法:

枚举某个点为出发点,以这个点为根做树形dp:f[i]=0/1表示以i为根的子树且以i为出发点先手能否必胜。

若i有一个儿子j满足f[j]=0,则f[i]=1,反之则f[i]=0。

这样就可以通过这道题。

考虑如何优化到O(n):

我们以1为根,设f[i]=0/1表示以i为整棵树且以i为出发点先手能否必胜,先dfs一次把只考虑儿子的f[i]求出来后,再dfs一次向父亲拓展即可。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;

const int N=1000005;

int n,last
,f
,a
,cnt;
struct edge{int to,next;}e[N*2];

int read()
{
int x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}

void addedge(int u,int v)
{
e[++cnt].to=v;e[cnt].next=last[u];last[u]=cnt;
e[++cnt].to=u;e[cnt].next=last[v];last[v]=cnt;
}

void dfs1(int x,int fa)
{
for (int i=last[x];i;i=e[i].next)
{
if (e[i].to==fa) continue;
dfs1(e[i].to,x);
if (!f[e[i].to]&&a[e[i].to]<a[x]) f[x]=1;
}
}

void dfs2(int x,int fa)
{
if (fa&&a[fa]<a[x]&&!f[fa]&&!f[x]) f[x]=1;
for (int i=last[x];i;i=e[i].next)
if (e[i].to!=fa) dfs2(e[i].to,x);
}

int main()
{
n=read();
for (int i=1;i<=n;i++) a[i]=read();
for (int i=1;i<n;i++)
{
int x=read(),y=read();
addedge(x,y);
}
dfs1(1,0);
dfs2(1,0);
for (int i=1;i<=n;i++) if (f[i]) printf("%d ",i);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: