【洛谷P4096】Eden 的博弈树

题目

题目链接:https://www.luogu.com.cn/problem/P4096

思路

\(f[x][0/1]\) 表示如果要确定点 \(x\) 是必败 / 必胜的话至少要确定多少个叶子。那么枚举子节点 \(v\),有

\[f[x][0]=\sum_{v\in son[x]}f[v][1] \]

\[f[x][1]=\min_{v\in son[x]}(f[v][0]) \]

然后求出来 \(f[1]\) 后再次分别递归找到能使 \(1\) 确定状态的叶子即可。
时间复杂度 \(O(n)\)

代码

#include <bits/stdc++.h>
using namespace std;

const int N=200010,Inf=1e9;
int n,tot,sum,ans,head[N],cnt[N],f[N][2];
bool flag;

struct edge
{
	int next,to;
}e[N];

void add(int from,int to)
{
	e[++tot].to=to;
	e[tot].next=head[from];
	head[from]=tot;
}

void dfs1(int x)
{
	if (head[x]==-1) f[x][0]=f[x][1]=1;
		else f[x][0]=0,f[x][1]=Inf;
	for (int i=head[x];~i;i=e[i].next)
	{
		dfs1(e[i].to);
		f[x][0]+=f[e[i].to][1];
		f[x][1]=min(f[x][1],f[e[i].to][0]);
	}
}

void dfs2(int x,int k)
{
	if (head[x]==-1) cnt[x]++;
	for (int i=head[x];~i;i=e[i].next)
	{
		int v=e[i].to;
		if (k && f[v][0]==f[x][1]) dfs2(v,0);
		if (!k) dfs2(v,1);
	}
}

int main()
{
	memset(head,-1,sizeof(head));
	scanf("%d",&n);
	for (int i=2,x;i<=n;i++)
	{
		scanf("%d",&x);
		add(x,i);
	}
	dfs1(1);
	dfs2(1,0); dfs2(1,1);
	for (int i=1;i<=n;i++)
	{
		if (cnt[i]==2) sum++,ans^=i;
		if (cnt[i]==2 && !flag) printf("%d ",i),flag=1;
	}
	printf("%d %d",sum,ans);
	return 0;
}
posted @ 2020-10-21 16:12  stoorz  阅读(120)  评论(0编辑  收藏  举报