BZOJ 2819: Nim

2819: Nim

Time Limit: 20 Sec  Memory Limit: 128 MB
Submit: 3596  Solved: 1348
[Submit][Status][Discuss]

Description

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

1.随机选两个堆v,u,询问若在v到u间的路径上的石子堆中玩Nim游戏,是否有必胜策略,如果有,vfleaking将会考虑将这些石子堆作为初始局面之一,用来坑玩家。
2.把堆v中的石子数变为k。

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

Input

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

对于100%的数据:
1≤N≤500000, 1≤Q≤500000, 0≤任何时候每堆石子的个数≤32767
其中有30%的数据:
石子堆组成了一条链,这3个点会导致你DFS时爆栈(也许你不用DFS?)。其它的数据DFS目测不会爆。

注意:石子数的范围是0到INT_MAX

Output

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

Sample Input

【样例输入】
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 Output

Yes
No
Yes
Yes
Yes

HINT

 

Source

湖北省队互测

总结:

nim游戏:若每堆的石子个数异或起来不为0则有必胜策略,否则没有

所以想到可以维护两点之间的异或和

手写栈(貌似bzoj不会爆栈

方法:

1.树剖板子

2.lca + dfs序 + 树状数组

讲一下方法2

求出dfs序

用树状数组来维护异或前缀和

及维护每个节点到根节点的异或和

然后每次查询答案为:query(x) ^ query(y) ^ a[lca(x, y)]

lca用倍增,树剖都可以

方法2比方法1少一个log

树链剖分

#include <bits/stdc++.h>

using namespace std;
const int maxn = 500005;
const int N = 4 * maxn;

int dfn[maxn], low[maxn], siz[maxn], xr[N], son[maxn], f[maxn], tot;
char ch[2]; int wt[maxn], dep[maxn], head[N], cnt = 1, top[maxn], a[maxn];
struct Node {
	int v, nxt;
} G[N]; int q, n;
void ins(int u, int v) {
	G[cnt] = (Node) {v, head[u]}; head[u] = cnt++;
}
void dfs1(int x, int fa, int deep) {
	dep[x] = deep; f[x] = fa; siz[x] = 1;
	int maxson = -1;
	for (int i = head[x]; i; i = G[i].nxt) {
		int v = G[i].v; if(v == fa) continue;
		dfs1(v, x, deep + 1); siz[x] += siz[v];
		if(siz[v] > maxson) maxson = siz[v], son[x] = v;
	}
}
void dfs2(int x, int topf) {
	dfn[x] = ++tot; wt[tot] = a[x]; top[x] = topf;
	if(son[x] != 0) dfs2(son[x], topf);
	for (int i = head[x]; i; i = G[i].nxt) {
		int v = G[i].v; if(v == f[x] || v == son[x]) continue;
		dfs2(v, v);
	} low[x] = tot;
}
void update(int o) {
	xr[o] = xr[o << 1] ^ xr[o << 1 | 1];
}
void build(int o, int l, int r) {
	if(l == r) { 
		xr[o] = wt[l]; return;
	} int mid = (l + r) >> 1;
	build(o << 1, l, mid); build(o << 1 | 1, mid + 1, r);
	update(o);
}
void modify(int o, int l, int r, int ql, int z) {
	if(l == r) {
		xr[o] = z; return;
	} int mid = (l + r) >> 1;
	if(ql <= mid) modify(o << 1, l, mid, ql, z);
	else modify(o << 1 | 1, mid + 1, r, ql, z);
	update(o);
}
int query(int o, int l, int r, int ql ,int qr) {
	if(ql <= l && r <= qr) return xr[o];
	int mid = (l + r) >> 1; int res = 0;
	if(ql <= mid) res ^= query(o << 1, l, mid, ql, qr);
	if(qr > mid) res ^= query(o << 1 | 1, mid + 1, r, ql, qr); 
    return res;
}
int Query(int x, int y) {
	int res = 0;
	while(top[x] != top[y]) {
		if(dep[top[x]] < dep[top[y]]) swap(x, y);
		res ^= query(1, 1, n, dfn[top[x]], dfn[x]); x = f[top[x]];
	} if(dep[x] > dep[y]) swap(x, y);
	res ^= query(1, 1, n, dfn[x], dfn[y]); return (res == 0);
}
int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
	for (int i = 1; i <= n - 1; ++i) {
		int x, y; scanf("%d%d", &x, &y); 
		ins(x, y); ins(y, x);
	}
	dfs1(1, -1, 0); dfs2(1, 1); build(1, 1, n);
	scanf("%d", &q);
	while(q--) {
		scanf("%s", ch);
		if(ch[0] == 'Q') {
			int x, y; scanf("%d%d", &x, &y); 
			(!Query(x, y)) ?printf("Yes\n") :printf("No\n");
		}
		else {
			int k, x; scanf("%d%d", &k, &x); modify(1, 1, n, dfn[k], x);
		}
	}
	return 0;
}

dfs序 + lca + 树状数组

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1000005;
const int N = 500005;
int head[maxn], cnt = 1, dfn[N], low[N], n;
int c[N], a[N], dep[N], f[N][21];
struct Node {
	int v, nxt;
} G[maxn]; int tot = 0, q; char ch[5];

void ins(int u, int v) {
	G[cnt] = (Node) {v, head[u]}; head[u] = cnt++;
}
void dfs(int x, int fa, int deep) {
	f[x][0] = fa; dep[x] = deep; dfn[x] = ++tot;
	for (int i = 1; i <= 20 && f[f[x][i - 1]][i - 1]; ++i) f[x][i] = f[f[x][i - 1]][i - 1];
	for (int i = head[x]; i; i = G[i].nxt) {
		int v = G[i].v; if(v == fa) continue;
		dfs(v, x, deep + 1);
	} low[x] = tot;
}
int lowbit(int x) {
	return x & (-x);
}
void modify(int x, int y) {
	for (int i = x; i <= n; i += lowbit(i)) c[i] ^= y;
}
int query(int x) {
	int ret = 0;
	for (int i = x; i; i -= lowbit(i)) ret ^= c[i];
	return ret;
}
int LCA(int x, int y) {
	if(dep[x] < dep[y]) swap(x, y);
	for (int i = 20; i >= 0; --i) {
		if(dep[f[x][i]] >= dep[y]) x = f[x][i];
	} if(x == y) return x;
	for (int i = 20; i >= 0; --i) {
		if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
	} return f[x][0];
}

int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
	for (int i = 1; i <= n - 1; ++i) {
		int x, y; scanf("%d%d", &x, &y);
		ins(x, y); ins(y, x);
	} 
	dfs(1, 0, 1);
	for (int i = 1; i <= n; ++i) {
		modify(dfn[i], a[i]); modify(low[i] + 1, a[i]);
	} scanf("%d", &q);
	while(q--) {
		int x, y;
		scanf("%s%d%d", ch, &x, &y); 
		if(ch[0] == 'Q') (query(dfn[x]) ^ query(dfn[y]) ^ a[LCA(x, y)]) ?puts("Yes") :puts("No");
		else {
			modify(dfn[x], a[x] ^ y); modify(low[x] + 1, a[x] ^ y); a[x] = y;
		}
	}
	return 0;
}

  

  

猜你喜欢

转载自www.cnblogs.com/oi-forever/p/9188314.html