Description
著名游戏设计师ljh,最近迷上了Nim。普通的Nim游戏为:两个人进行游戏,N堆石子,每回合可以取其中某一堆的任意多个,可以取完,但不可以不取。谁不能取谁输。这个游戏是有必胜策略的。于是ljh决定写一个玩Nim游戏的平台来坑玩家。
为了设计漂亮一点的初始局面,ljh用以下方式来找灵感:拿出很多石子,把它们聚成一堆一堆的,对每一堆编号1234...n在堆与堆间连边,没有自环与重边,从任意堆到任意堆都只有唯一一条路径可到达。然后他不停地进行如下操作:
1.随机选两个堆v,u,询问若在v到u间的路径上的石子堆中玩Nim游戏,是否有必胜策略,如果有,ljh将会考虑将这些石子堆作为初始局面之一,用来坑玩家。
2.把堆v中的石子数变为k。
由于ljh太懒了,他懒得自己动手了。请写个程序帮帮他吧。
Input
第一行一个数n,表示有多少堆石子。
接下来的一行,第i个数表示第i堆里有多少石子。
接下来n-1行,每行两个数vu,代表vu间有一条边直接相连。
接下来一个数q,代表操作的个数。
接下来q行,每行开始有一个字符:
如果是Q,那么后面有两个数vu,询问若在v到u间的路径上的石子堆中玩Nim游戏,是否有必胜策略。
如果是C,那么后面有两个数vk,代表把堆v中的石子数变为k
Output
对于每个Q,输出一行Yes或No,代表对询问的回答。
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≤100,1≤Q≤100
对于100%的数据,1≤N≤500000 1≤Q≤500000 0≤任何时候每堆石子的个数≤max longint
其中有30%的数据,石子堆组成了一条链
注意:石子数的范围是0到max 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;
}