#194-[树链剖分,博弈论] Nim游戏

Description

著名游戏设计师ljh,最近迷上了Nim。普通的Nim游戏为:两个人进行游戏,N堆石子,每回合可以取其中某一堆的任意多个,可以取完,但不可以不取。谁不能取谁输。这个游戏是有必胜策略的。于是ljh决定写一个玩Nim游戏的平台来坑玩家。
为了设计漂亮一点的初始局面,ljh用以下方式来找灵感:拿出很多石子,把它们聚成一堆一堆的,对每一堆编号1234...n在堆与堆间连边,没有自环与重边,从任意堆到任意堆都只有唯一一条路径可到达。然后他不停地进行如下操作:
1.随机选两个堆vu,询问若在vu间的路径上的石子堆中玩Nim游戏,是否有必胜策略,如果有,ljh将会考虑将这些石子堆作为初始局面之一,用来坑玩家。
2.把堆v中的石子数变为k

由于ljh太懒了,他懒得自己动手了。请写个程序帮帮他吧。


 

Input

第一行一个数n,表示有多少堆石子。
接下来的一行,第i个数表示第i堆里有多少石子。
接下来n-1行,每行两个数vu,代表vu间有一条边直接相连。
接下来一个数q,代表操作的个数。
接下来q行,每行开始有一个字符:
如果是Q,那么后面有两个数vu,询问若在vu间的路径上的石子堆中玩Nim游戏,是否有必胜策略。
如果是C,那么后面有两个数vk,代表把堆v中的石子数变为k

Output

对于每个Q,输出一行YesNo,代表对询问的回答。

 

5
1 3 5 2 5
1 5
3 5
2 5
1 4
6
Q 1 2
Q 3 5
C 3 7
Q 1 2
Q 2 4
Q 5 3
  • Sample Input

Yes
No
Yes
Yes
Yes
  • Sample Output

HINT

对于30%的数据,1≤N≤1001≤Q≤100
对于100%的数据,1≤N≤500000 1≤Q≤500000 0≤任何时候每堆石子的个数max longint
其中有30%的数据,石子堆组成了一条链

注意:石子数的范围是0max longint

 

Source/Category

树链剖分 

一眼看——树链剖分

再一看——博弈论?毒瘤?

如果所有数异或到一起不为0,先手就有必胜策略。

(并不知道原理)

#include <iostream>
#include <cstdio>
#include <vector>

using namespace std;

const int SIZE = 500010;

vector<int> ved[SIZE];
int n, a[SIZE], dp[SIZE<<2], father[SIZE], depth[SIZE], tsize[SIZE], son[SIZE], dfn[SIZE], revdfn[SIZE], top[SIZE], dfstime;

/* 维护线段树 */

void buildtree(int rt, int l, int r) { // 建树
	if (l==r) {
		dp[rt] = a[revdfn[l]]; return;
	}
	int m = (l+r) >> 1; buildtree(rt<<1, l, m); buildtree(rt<<1|1, m+1, r);
	dp[rt] = dp[rt<<1] ^ dp[rt<<1|1];
	
	return;
}

void update(int rt, int l, int r, int x, int val) { // 更新(没有懒标记,舒服)
	if (l==r) {
		dp[rt] = val; return;
	}
	int m = (l+r) >> 1; if (x<=m) update(rt<<1, l, m, x, val); else update(rt<<1|1, m + 1, r, x, val);
	dp[rt] = dp[rt<<1] ^ dp[rt<<1|1];
	
	return;
}

int query(int rt, int l, int r, int x, int y) { // 查询区间
	if ((x<=l) && (r<=y)) return dp[rt];
	int m = (l+r) >> 1, res = 0; if (x<=m) res ^= query(rt<<1, l, m, x, y); if (y>m) res ^= query(rt<<1|1, m+1, r, x, y);
	
	return res;
}

/* 树链剖分 */

void dfs(int u, int fa, int dep) { // 第一次深搜,重儿子深度大小
	father[u] = fa; depth[u] = dep; tsize[u] = 1; son[u] = 0;
	for (unsigned int i=0; i<ved[u].size(); ++i) {
		int v = ved[u][i];
		if (v!=fa) {
			dfs(v, u, dep+1);
			tsize[u] += tsize[v];
			if ((!son[u]) || (tsize[son[u]]<tsize[v])) son[u] = v;
		}
	}
	
	return;
}

void dfs2(int u, int tp) // 第二次深搜,第几个访问和重链顶端
{
	top[u] = tp; dfn[u] = ++dfstime; revdfn[dfstime] = u;
	if (son[u]) {
		dfs2(son[u], tp);
		for (unsigned int i=0; i<ved[u].size(); ++i) {
			int v = ved[u][i];
			if ((v!=father[u]) && (v!=son[u])) dfs2(v, v);
		}
	}
	
	return;
}

int tcpquery(int x, int y) // 查询异或到一起的值
{
	int res = 0;
	
	while (top[x]!=top[y]) {
		if (depth[top[x]]<depth[top[y]]) swap(x, y);
		res ^= query(1, 1, n, dfn[top[x]], dfn[x]); x = father[top[x]];
	}
	if (depth[x]>depth[y]) swap(x, y);
	
	return res ^ query(1, 1, n, dfn[x], dfn[y]);
}

int main(void) {	
	scanf("%d", &n);
	for (int i=1; i<=n; ++i) scanf("%d", &a[i]);
	for (int i=1; i<n; ++i) {
		int u, v; scanf("%d%d", &u, &v);
		ved[u].push_back(v); ved[v].push_back(u);
	}
	dfs(1, 0, 1); dfs2(1, 1); buildtree(1, 1, n);
	int q; scanf("%d", &q);
	while (q--) {
		int x, y; char op[3]; scanf("%s%d%d", op, &x, &y);
		if (op[0] == 'Q') {
			if (tcpquery(x, y)) printf("Yes\n"); else printf("No\n");
		}
		else update(1, 1, n, dfn[x], y);
	}
	
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值