题意:
给出一棵树,每个节点有黑/白两种颜色,两个人轮流操作:
1、选中一个白色节点,并选择其的一部分子集
2、反转这个节点以及选中的子集的颜色。
分析:
首先,这是一个Nim游戏,因此可以使用SG函数与SG定理
因此,我们只需要求出每个最高的白点的Nim值,再将其异或即可。
首先,对于根为黑色的节点,其Nim值相当于其各个儿子的Nim值的异或和,很容易想明白这一点:因为根为黑色,所以不可能选中当前节点,因此相当于将这个点所有不同子树都视为不同的游戏,而这个节点表示这些游戏的和。
注:以下内容我仍未太清楚,只能模糊地描述一下
对于根是白色的节点,就有所不同了,因为可以任意改变其后代的颜色,所以它可以转移到任意一种子节点的状态。首先,考虑其不改变子节点颜色的情况,那么它也要加上所有子树的异或和。
再来考虑要改变其子节点颜色的情况,设其深度最大的子节点的深度为
d
e
e
p
i
deep_i
deepi
则
N
i
m
i
Nim_i
Nimi还需要异或
2
d
e
e
p
i
2^{deep_i}
2deepi
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<iostream>
#define SF scanf
#define PF printf
#define MAXN 60
using namespace std;
int fa[MAXN];
int col[MAXN],dep[MAXN],n;
vector<int> a[MAXN];
long long ans,nim[MAXN];
void dfs(int x){
dep[x]=0;
for(int i=0;i<a[x].size();i++){
dfs(a[x][i]);
dep[x]=max(dep[x],dep[a[x][i]]);
nim[x]^=nim[a[x][i]];
}
dep[x]++;
if(col[x]==1)
nim[x]^=(1ll<<dep[x]);
}
class GameWithTree{
public:
string winner(vector<int> fax,string co){
n=fax.size()+1;
for(int i=0;i<n-1;i++){
fa[i+1]=fax[i];
a[fa[i+1]].push_back(i+1);
}
for(int i=0;i<n;i++)
if(co[i]=='W')
col[i]=1;
dfs(0);
if(nim[0]==0)
return "Petya";
return "Masha";
}
}test;
/*int n1,u;
vector<int> rr;
string rs;
int main(){
SF("%d",&n1);
n1++;
for(int i=1;i<n1;i++){
SF("%d",&u);
rr.push_back(u);
}
cin>> rs;
cout << test.winner(rr,rs) <<endl;
}*/