BZOJ 4202: 石子游戲 SG定理+LCT

4202: 石子游戲

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 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

Sample Output

No

HINT

對於100%的數據, n,t <= 50000。

同時,保證任何時刻樹中節點數目和編號都不會超過 100000。

其餘數據的範圍皆在int範圍內。

Source

[Submit][Status][Discuss]

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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章