【博弈】GameWithTree

题意:

给出一棵树,每个节点有黑/白两种颜色,两个人轮流操作:
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;
}*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值