Game of CS(green博弈)

ps:因为我也是这两天学的树上博弈,可能有的地方描述不是很准确,如有错误之处,欢迎指出

前置知识:green博弈:给定一棵树,两人轮流进行删边,删除改边后,由改边与根节点联通的子树也随之删除,当无边可删者输。
green博弈解法:详细推导过程点这里,把每一条树链拿出来,不难发现在这条链上选边删除,类似于取石子nim游戏中的取走一定数量的石子,而且有SG[x]=他的子节点数量,所以对叶子节点来说SG[x]=0,而叶子结点的父节点的SG[y]=SG[x]+1,上述讨论的是单链,现在对整棵树讨论,如果是叶子节点,则SG[x]=0;然后从树的最底层一层一层往上走,不断更新SG[y],SG[y]^=(SG[x]+1)(SG定理)

但是上面讨论的是树的权值都为1的情况下,下面开始推导当权值不为1的情况下的SG,这里需要用到SG函数的定义,即SG(u)=mex{SG(v)},u→v ,推导思路来源,上面的链接里有带图的推理过程,但本人画图属实菜鸡,推导过程可以看链接里的,我这里自己推给自己看

简单推导:

定义dp[x][y]为AB边的权值为x,y是与B相连的树链的长度(即SG[B]=y)以时节点A的SG值;
①当x=0时,即A就是根节点,此情况下无法操作,既有SG[A]=0,dp[0][y]=0;
②当x=1时,此时就是裸green博弈,则dp[1][x]=x+1;

当x==1时
dp[1][y]=SG[y]+1

当x=2时
dp[2][0]=mex(dp[1][0])=mex(1)=0=SG[B];
dp[2][1]=mex(dp[1][1],dp[2][0])=mex(2,0)=1=SG[B];
dp[2][2]=mex(dp[1][2],dp[2][0],dp[2][1])=mex(3,0,1)=2=SG[B]…


dp[2][y]=mex(dp[1][y],dp[2,0],dp[2][1]…dp[2][y-1])=mex(y+1,0,1,…y-1)=y=SG[B];
所以但权值为偶数时SG[A]=SG[B]

当x=3时
dp[3][0]=mex(dp[2][0])=1;
dp[3][1]=mex(dp[2][1],dp[3][0])=mex(1,1)=0;
dp[3][2]=mex(dp[2][2],dp[3][0],dp[3][1])=mex(2,1,0)=3;

dp[3][x]=mex(dp[2][x],dp[3][0],dp[3][1]…dp[3][x-1])=mex(x,1,0,3,2…t)(t=((x-1)&1)?x-2:x)=((x-1)&1)?x-1:x+1;

当x=…(逃)

if (val == 1) sg[from] ^= sg[to];
 else sg[from]^=(sg[to]^(val%2));

小知识`x ^ 1 == (x & 1) ? x - 1 : x + 1;

好了,完事

AC代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<functional>
#include<vector>

using namespace std;

const int maxn = 10100;

struct edge {
	int to;
	int nxt;
	int val;
}date[maxn];

int head[maxn];
int sg[maxn];

int cnt;

void add(int u, int v,int w) {
	date[++cnt].to = v;
	date[cnt].nxt = head[u];
	date[cnt].val = w;
	head[u] = cnt;
}

void dfs(int x,int y) {
	sg[x] = 0;
	for (int i = head[x]; i!=-1; i = date[i].nxt) {
		int v = date[i].to;
		if (v == y)	continue;
		dfs(v,x);
		if (date[i].val == 1) sg[x] ^=(sg[v] + 1);
		else sg[x] ^= (sg[v] ^ (date[i].val % 2));
	}

}

int main() {
	int t;
	scanf("%d", &t);
	for (int k = 1; k <= t; k++) {
		int n;
		cnt = 0;
		memset(head, -1, sizeof(head));
		//memset(sg, 0, sizeof(sg));
		scanf("%d", &n);
		for (int i = 1; i < n; i++) {
			int u, v, w;
			scanf("%d %d %d", &u, &v, &w);
			add(u, v, w);
			add(v, u, w);
		}
		dfs(0, 0);
		if (sg[0]) printf("Case %d: Emily\n", k);

		else printf("Case %d: Jolly\n", k);
	}
	
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值