> 文章列表 > 【bzoj 4202】石子游戏(博弈论+LCT)

【bzoj 4202】石子游戏(博弈论+LCT)

【bzoj 4202】石子游戏(博弈论+LCT)

题目

石子游戏是大家都很喜欢玩的一类游戏,这类游戏通常与石子的移动和取舍有关,往往可以让人在游戏中获得不少的乐趣。

有一类树上石子游戏的规则是这样的:在一棵有根树上,每个节点都有着一定数目的石子,两个玩家轮流进行游戏。每次,每个玩家可以把不超过 mmm 个的石子移动到它的父亲上。显然,根节点没有父亲,故每个石子一旦移动到根节点便无法再次移动。问题是以某个节点为根的子树进行这样的游戏,是否存在先手必胜策略。

为了增加这个游戏的难度,我们对这个游戏进行一些小小的修改。现在,我们的这棵树可能会长出新的节点。同时,每个节点上的石子数目可能会变化。请问,以某个节点为根的子树进行这样的石子游戏,是否存在先手必胜策略。

思路

我们先考虑不修改树时的题目。

本质上就是一个阶梯博弈和巴什博弈。

对于以该节点为根的子树,我们就将它的子树中所有离它距离为奇数的节点的 SGSGSG 函数异或起来,如果为零,就先手必输,否则先手必胜。

再来考虑修改。

修改可以看成修改节点到根节点的路径上跟它距离为奇数的节点的答案异或上该节点的 SGSGSG

就是裸的 LCT。

对于一个点到它子树中的点的距离的奇偶性可以看两个点到根的奇偶性来判断。

可以用一个懒标记来修改,其中懒标记分别记录修改的点到根节点距离的奇偶不同的异或和即可。

具体实现可以看代码。

代码

#include <bits/stdc++.h>
using namespace std;
int n, m, T, sum, ans[500005], val[500005], bz[500005][3], fa[500005], d[500005], last[500005], Ecnt, son[500005][3];
int cnt = 0;
struct Edge { int to, next; } E[100005];
void addedge(int u, int v) { Ecnt++, E[Ecnt].next = last[u], last[u] = Ecnt, E[Ecnt].to = v; }
bool pd(int x) { return son[fa[x]][0] == x || son[fa[x]][1] == x; }
bool pdd(int x) { return son[fa[x]][1] == x; }
void rotato(int x) {int y = fa[x], z = fa[y], p = pdd(x);if (pd(y))son[z][pdd(y)] = x;fa[x] = z, fa[y] = x;son[y][p] = son[x][p ^ 1], fa[son[x][p ^ 1]] = y;son[x][p ^ 1] = y;
}
void pushdown(int x) {ans[x] = ans[x] ^ bz[x][(d[x] & 1) ^ 1];bz[son[x][1]][1] ^= bz[x][1], bz[son[x][0]][1] ^= bz[x][1], bz[x][1] = 0;bz[son[x][1]][0] ^= bz[x][0], bz[son[x][0]][0] ^= bz[x][0], bz[x][0] = 0;
}
void push(int x) {if (pd(x))push(fa[x]);pushdown(x);
}
void splay(int x) {push(x);for (int y; pd(x); rotato(x)) {y = fa[x];if (pd(y))rotato(pdd(x) == pdd(y) ? y : x);}
}
void access(int x) {int last = 0; while (x) {splay(x), son[x][1] = last;last = x, x = fa[x];}
}
void find(int x) {for (int xy = last[x]; xy; xy = E[xy].next)if (E[xy].to != fa[x])d[E[xy].to] = d[x] + 1, fa[E[xy].to] = x, d[E[xy].to], find(E[xy].to);
}
int main() {scanf("%d%d", &n, &m), m++;for (int i = 1; i <= n; i++)scanf("%d", &val[i]), val[i] = val[i] % m;for (int i = 1, u, v; i < n; i++)scanf("%d%d", &u, &v), addedge(u, v), addedge(v, u);find(1);for (int i = 1; i <= n; i++) {access(i);splay(i);bz[i][d[i] & 1] = val[i];}scanf("%d", &T);while (T--) {int id, u, v, x;scanf("%d", &id);if (id == 1) {scanf("%d", &x), x = x ^ sum;push(x);if (ans[x])printf("Yes\\n"), sum++;elseprintf("No\\n");}else if (id == 2) {scanf("%d%d", &u, &x), u = u ^ sum, x = x ^ sum, x = x % m;access(u), splay(u), bz[u][d[u] & 1] = val[u] ^ x, val[u] = x;}else {scanf("%d%d%d", &u, &v, &x), u = u ^ sum, v = v ^ sum, x = x ^ sum, val[v] = x % m;fa[v] = u, d[v] = d[u] + 1, access(v), splay(v), bz[v][d[v] & 1] = val[v];}}return 0;
}