[BZOJ 4202石子游戏] 博弈 + LCT
博弈博弈博弈 owo
Problem 4202. -- 石子游戏
Submit: 57 Solved: 20
[Submit][Status][Discuss]
4202: 石子游戏
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 57 Solved: 20
[Submit][Status][Discuss]
Description
石子游戏是大家都很喜欢玩的一类游戏,这类游戏通常与石子的移动和取
舍有关,往往可以让人在游戏中获得不少的乐趣。
有一类树上石子游戏的规则是这样的:在一棵有根树上,每个节点都有着
一定数目的石子,两个玩家轮流进行游戏。每次,每个玩家可以把不超过 m 个
的石子移动到它的父亲上。显然,根节点没有父亲,故每个石子一旦移动到根
节点便无法再次移动。问题是以某个节点为根的子树进行这样的游戏,是否存
在先手必胜策略。
为了增加这个游戏的难度,我们对这个游戏进行一些小小的修改。现在,
我们的这棵树可能会长出新的节点。同时,每个节点上的石子数目可能会变化。
请问,以某个节点为根的子树进行这样的石子游戏,是否存在先手必胜策略。
Input
第一行包含两个数字 n 和 m,表示初始时有 n 个节点,
每次移动不能超过 m 个。
第二行 n 个正整数 a1,a2...an,表示初始时候的石子数量,其中 1 号节
点为根节点。
接下来 n 1 行,每行两个整数 u 和 v,表示有一条从 u 到 v 的边。
接下来一行一个数 t,表示操作的数目。
接下来 t 行,每行代表一个操作,每行的第一个数字代表操作类型,其中:
若为 1,后跟一个数字 v,表示询问在 v 的子树中做游戏先手是否必胜。
若为 2,后跟两个数字 x, y 表示将节点 x 的石子数修改为 y。
若为 3,后跟三个数字 u, v, x,表示为 u 节点添加一个儿子 v,初始石
子数为 x。
Output
对于每一个询问,若先手必胜输出“Yes”,否则输出“No”。 注,数据进行了强制在线处理,对于每一个操作,除类型名外,都需要异 或之前回答为“Yes“的数目。
Sample Input
2 1000 0 0 1 2 1 1 1Sample Output
No HINT 对于100%的数据, n,t <= 50000。 同时,保证任何时刻树中节点数目和编号都不会超过 100000。 其余数据的范围皆在int范围内。题解
对于跳到某一个根,我们可以想到阶梯博弈---->将所有奇数高度台阶的sg进行nim游戏,这个就是奇数高度的树上的sg值进行nim了.其阶梯博弈的意义就是如果先手移动偶数台阶的棋子,那么后手必定将移动过来的棋子再移一格使奇偶不变,最终局面不发生改变. 而对于每次不移动超过m,结论就是%(m+1) 那么这道题就是开两颗LCT,一棵树维护奇数深度的所有信息,一棵维护偶数深度的所有信息,每次修改链修改,每次查询子树查询,.code
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#define zig(x) zigzag(x,1)
#define zag(x) zigzag(x,2)
using namespace std;
const int maxn = 3e5+5;
int n,m;
int A[maxn];
struct LCT{
int ls[maxn],rs[maxn],fa[maxn],val[maxn],laz[maxn],rev[maxn];
void ptd(int x) {
if(laz[x]) {
val[ls[x]]^=laz[x];
val[rs[x]]^=laz[x];
laz[ls[x]]^=laz[x];
laz[rs[x]]^=laz[x];
laz[x] = 0;
}
if(rev[x]) {
rev[x]^=1;
rev[ls[x]]^=1; rev[rs[x]]^=1;
swap(ls[x],rs[x]);
}
}
void ptdall(int x) {
if(!isroot(x)) ptdall(fa[x]);
ptd(x);
}
bool isroot(int x) { return ls[fa[x]]!=x&&rs[fa[x]]!=x; }
void zigzag(int x,int dft) {
int y = fa[x] , z = fa[y];
if(!isroot(y)) {
if(ls[z]==y) ls[z] = x; else rs[z] = x;
}
fa[x] = z; fa[y] = x;
if(dft==1) {
ls[y] = rs[x];
fa[ls[y]] = y;
rs[x] = y;
} else {
rs[y] = ls[x];
fa[rs[y]] = y;
ls[x] = y;
}
}
void splay(int x) {
int y , z;
ptdall(x);
while(!isroot(x)) {
y = fa[x] ; z = fa[y];
if(isroot(y)) {
if(ls[y]==x) zig(x); else zag(x);
} else{
if(ls[z]==y) {
if(ls[y]==x) zig(y),zig(x);
else zag(x),zig(x);
} else {
if(rs[y]==x) zag(y),zag(x);
else zig(x),zag(x);
}
}
}
}
void acc(int x) {
for(int y=0;x;y=x,x=fa[x]) {
splay(x);
rs[x] = y;
}
}
void setroot(int x) {
acc(x); splay(x); rev[x]^=1;
}
void link(int x,int ff) {
setroot(x); fa[x] = ff;
}
void change(int x,int O) {
setroot(1); acc(x);
splay(x); laz[x]^=O; val[x]^=O;
return;
}
int query(int x) {
setroot(1); acc(x); splay(x);
return val[x];
}
}odd,eve;
int la[maxn],en[maxn],nt[maxn],owo,dep[maxn];
void adg(int x,int y) {
en[++owo]=y; nt[owo]=la[x]; la[x]=owo;
}
void DFS(int x,int ba){
dep[x] = dep[ba]+1;
for(int it=la[x];it;it=nt[it]) {
int y = en[it];
if(y==ba) continue;
DFS(y,x);
}
if(ba) odd.link(x,ba),eve.link(x,ba);
}
char buf[1<<20],*p1,*p2;
#define GC (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?0:*p1++)
inline int R()
{
char t=GC;
int x=0;
while(!isdigit(t)) t=GC;
while(isdigit(t)) x=x*10+t-48,t=GC;
return x;
}
int main() {
n=R(); m = R();
int cnt = 0;
for(int i=1;i<=n;i++) {
A[i]=R(); A[i]%=(m+1);
}
for(int i=1;i<n;i++) {
int x,y; x=R();y=R();
adg(x,y); adg(y,x);
}
DFS(1,0);
for(int i=1;i<=n;i++) {
if(dep[i]&1) odd.change(i,A[i]);
else eve.change(i,A[i]);
}
int t; t=R();
int op,x,y,z;
int WOC = 0;
for(int i=1;i<=t;i++) {
op=R();
if(op==1) {
x=R();x^=cnt;
int oo = 0;
if(dep[x]&1) oo = eve.query(x);
else oo = odd.query(x);
// cerr<<(++WOC)<<endl;
if(oo!=0) cnt++,puts("Yes");
else puts("No");
} else if(op==2) {
x=R();y=R();
x^=cnt; y^=cnt;
y%=(m+1);
if(dep[x]&1) odd.change(x,A[x]^y),A[x]=y;
else eve.change(x,A[x]^y),A[x]=y;
} else {
x=R();y=R();z=R();
x^=cnt; y^=cnt; z^=cnt; z%=(m+1);
dep[y] = dep[x] + 1; A[y] = z;
odd.link(x,y); eve.link(y,x);
if(dep[y]&1) odd.change(y,z);
else eve.change(y,z);
}
}
}