4202: 石子游戲
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 37 Solved: 15
[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 1
0 0
1 2
1
1 1
Sample Output
No
HINT
對於100%的數據, n,t <= 50000。
同時,保證任何時刻樹中節點數目和編號都不會超過 100000。
其餘數據的範圍皆在int範圍內。
Source
Sol:
樹上博弈,先考慮SG那套理論
取石子向上移動?階梯NIM -- 只考慮奇數堆做NIM即可,對手奇->偶相當於取走石子,設法將異或和再次變成0,對手偶->奇相當於添加石子,再把等量石子奇->偶即可
樹上階梯NIM? 將根節點深度看做0,只考慮奇深度即可
移動不超過m個? Bash博弈,SG[x] = Stone[x] mod (m+1)
必勝條件? SG定理,子游戲SG函數值異或和爲0 -> 先手必敗 否則先手必勝。
好了,現在考慮如何實現修改。
離線的話,單點修改,子樹查詢,很容易先到dfs序+樹狀數組/線段樹什麼的,添加兒子可以先添加好後處理dfs序再做。。。
在線的話就行不通了,換一個角度考慮子樹查詢:一個點修改影響的點一定在她到根的路徑上 -> 轉化爲鏈上修改
加兒子?Link_Cut_Tree來一發
詢問奇數深度的點要查詢子樹內偶數深度點的異或和,相反同理。
開兩棵LCT,一棵僅接受奇數深度點的修改,一棵僅接受偶數,同時維護樹的形態。
似乎不是很機智的做法,不過我太菜了只能想到這種暴力的做法QAQ
Code:
#include<bits/stdc++.h>
#define debug(x) cout<<#x<<"="<<x<<endl
using namespace std;
const int maxn = 100009;
int tot;
int n,m,q;
struct LCT
{
int fa[maxn],son[maxn][2],val[maxn],tag[maxn];
stack<int>S;
bool rev[maxn];
inline bool is_root(int x)
{
if(x==0) return 1;
return (son[fa[x]][0]!=x)&&(son[fa[x]][1]!=x);
}
void down(int x)
{
if(tag[x])
{
val[son[x][0]]^=tag[x];
tag[son[x][0]]^=tag[x];
val[son[x][1]]^=tag[x];
tag[son[x][1]]^=tag[x];
tag[x]=0;
}
if(rev[x])
{
swap(son[x][0],son[x][1]);
rev[son[x][0]]^=1;
rev[son[x][1]]^=1;
rev[x]=0;
}
}
inline void rotate(int x)
{
int y=fa[x],z=fa[y],l,r;
if(son[y][0]==x) l=0;else l=1;r=l^1;
if(!is_root(y)) if(son[z][0]==y) son[z][0]=x;
else son[z][1]=x;
fa[x]=z;fa[y]=x;fa[son[x][r]]=y;
son[y][l]=son[x][r];son[x][r]=y;
}
void splay(int x)
{
int y=x,z;
while(!is_root(y)) S.push(y),y=fa[y];S.push(y);
while(!S.empty()) down(S.top()),S.pop();
while(!is_root(x))
{
y=fa[x];z=fa[y];
if(!is_root(y)) if((son[y][0]==x)^(son[z][0]==y)) rotate(x);
else rotate(y);
rotate(x);
}
}
void access(int x)
{
int t=0;
while(x)
{
splay(x);
son[x][1]=t;
t=x;x=fa[x];
}
}
void make_root(int x)
{
access(x);splay(x);rev[x]^=1;
}
void link(int x,int y)
{
make_root(x);fa[x]=y;
}
void change(int x,int y)
{
make_root(1);access(x);
splay(x);tag[x]^=y;val[x]^=y;
}
int query(int x)
{
make_root(1);access(x);splay(x);
return val[x];
}
}eve,odd;
int first[maxn];
struct edg
{
int next;
int to;
}e[maxn<<1];
int e_sum;
int a[maxn],id[maxn*10],dep[maxn];
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
inline void add_edg(int x,int y)
{
e_sum++;
e[e_sum].next=first[x];
first[x]=e_sum;
e[e_sum].to=y;
}
void dfs(int x,int fa)
{
dep[x]=dep[fa]+1;
for(int i=first[x];i;i=e[i].next)
{
int w=e[i].to;;
if(w==fa) continue;
dfs(w,x);
}
}
int main()
{
n=read();m=read();tot=n;
for(int i=1;i<=n;i++) a[i]=read()%(m+1),id[i]=i;
for(int i=1;i<n;i++)
{
int x=read(),y=read();
add_edg(x,y);add_edg(y,x);
eve.link(x,y);odd.link(x,y);
}
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]);
}
q=read();
int cnt=0;
while(q--)
{
int opt=read();
if(opt==1)
{
int x=read()^cnt,sg;x=id[x];
if(dep[x]&1) sg=eve.query(x);
else sg=odd.query(x);
if(sg) puts("Yes"),cnt++;
else puts("No");
}
if(opt==2)
{
int x=read()^cnt,y=(read()^cnt)%(m+1);x=id[x];
if(dep[x]&1) odd.change(x,a[x]),odd.change(x,y);
else eve.change(x,a[x]),eve.change(x,y);
a[x]=y;
}
if(opt==3)
{
int x=read()^cnt,y=read()^cnt,z=(read()^cnt)%(m+1);
x=id[x];id[y]=++tot;y=id[y];dep[y]=dep[x]+1;a[y]=z;
eve.link(x,y);odd.link(x,y);
if(dep[y]&1) odd.change(y,z);
else eve.change(y,z);
}
}
return 0;
}