洛谷 P4096 [HEOI2013] Eden 的博弈树,一种由 xzm 大神提供的更简洁做法。
首先需要从下往上求出以 \(i\) 为根的子树先后手的最小必胜集合大小,记为 \(f_{i,0/1}\)(\(0\) 为先手,\(1\) 为后手)。此外在转移同时维护该子树内的三个问题的答案。
对于先手而言,只要取一个儿子能必胜就能获胜,显然就是取所有儿子中的后手最小必胜集合大小最小的,\(f_{i,0}=\min f_{son,1}\);
对于后手而言,需要取使所有儿子先手都能必胜才能获胜,\(f_{i,1}=\sum f_{son,0}\)。
注意到子树内先手最小必胜集合一定是后手最小必胜集合的子集,故关键节点集合即两者交集就是先手必胜集合,子树内答案就是将所有能贡献到先手的子结点(满足 \(f_{j\in son,1}=f_{i,0}\) 的儿子 \(j\))的答案合并(\(a_i=\min a_j,b_i=\sum b_j,c_i=\bigoplus c_j\))。
输出根节点答案即可。嗯对,做完了。
#include <bits/stdc++.h>
#define mem(a,v) memset(a,v,sizeof(a))
#define endl '\n'
#define FILE(x) freopen(x".in","r",stdin),freopen(x".out","w",stdout);
#define pii pair<int,int>
#define pll pair<long long,long long>
#define st first
#define nd second
#define pb push_back
using namespace std;
using ll=long long;
using lld=long double;
const int N=2e5+10;
int n,fa[N],f[N][2],a[N],b[N],c[N];
vector<int>g[N];
void dfs(int u){if(!g[u].size()){f[u][1]=f[u][0]=1,a[u]=u,b[u]=1,c[u]=u;return;}f[u][0]=INT_MAX;for(int v:g[u]){dfs(v);if(f[v][1]<f[u][0])f[u][0]=f[v][1],a[u]=a[v],b[u]=b[v],c[u]=c[v];else if(f[v][1]==f[u][0])a[u]=min(a[v],a[u]),b[u]+=b[v],c[u]^=c[v];f[u][1]+=f[v][0];}
}
int main(){ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);FILE("game");cin>>n;for(int i=2;i<=n;i++)cin>>fa[i],g[fa[i]].pb(i);dfs(1);cout<<a[1]<<' '<<b[1]<<' '<<c[1];return 0;
}