题目
题目链接:https://www.luogu.com.cn/problem/P4096
思路
设 (f[x][0/1]) 表示如果要确定点 (x) 是必败 / 必胜的话至少要确定多少个叶子。那么枚举子节点 (v),有
[f[x][0]=sum_{vin son[x]}f[v][1]
]
[f[x][1]=min_{vin 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;
}