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

解法:裸树剖,网上说要人工栈,我直接递归剖分过了啊。。不会人工栈,但是卡栈的可能性很小?

///BZOJ 2819 #include <bits/stdc++.h>
using namespace std;
const int maxn = 1000010;
struct edge{int v,next;}E[maxn*2];
struct seg{int l,r,Xor;}tree[maxn*4];
int n,m,tot,edgecnt,tim, root;
int num[maxn],siz[maxn],top[maxn],son[maxn],dep[maxn],tid[maxn],fa[maxn],head[maxn];
void init(){
    memset(head,-1,sizeof(head));
    memset(son,-1,sizeof(son));
    edgecnt=tim=0;
}
void addedge(int u, int v){
    E[edgecnt].v=v,E[edgecnt].next=head[u],head[u]=edgecnt++;
}
void dfs1(int u, int father, int d){
    dep[u]=d;
    fa[u]=father;
    siz[u]=1;
    for(int i=head[u];~i;i=E[i].next){
        int v=E[i].v;
        if(v==father) continue;
        dfs1(v,u,d+1);
        siz[u]+=siz[v];
        if(son[u]==-1||siz[v]>siz[son[u]]) son[u]=v;
    }
}
void dfs2(int u, int tp){
    top[u]=tp;
    tid[u]=++tim;
    if(son[u]==-1) return;
    dfs2(son[u],tp);
    for(int i=head[u];~i;i=E[i].next){
        int v=E[i].v;
        if(v!=son[u]&&v!=fa[u]) dfs2(v,v);
    }
}
void pushup(int rt){
    tree[rt].Xor=tree[rt*2].Xor^tree[rt*2+1].Xor;
}
void build(int l, int r, int rt){
    tree[rt].l=l,tree[rt].r=r,tree[rt].Xor=0;
    if(l==r) return;
    int mid=(l+r)/2;
    build(l,mid,rt*2);
    build(mid+1,r,rt*2+1);
}
void update(int pos, int v, int rt){
    if(tree[rt].l==tree[rt].r){
        tree[rt].Xor=v;
        return;
    }
    int mid=(tree[rt].l+tree[rt].r)/2;
    if(pos<=mid) update(pos,v,rt*2);
    else update(pos,v,rt*2+1);
    pushup(rt);
}
int query(int L, int R, int rt){
    if(L==tree[rt].l&&tree[rt].r==R){
        return tree[rt].Xor;
    }
    int mid=(tree[rt].l+tree[rt].r)/2;
    if(R<=mid) return query(L,R,rt*2);
    else if(L>mid) return query(L,R,rt*2+1);
    else return query(L,mid,rt*2)^query(mid+1,R,rt*2+1);
}
int solve(int x, int y){
    int ans = 0;
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        ans^=query(tid[top[x]],tid[x],1);
        x=fa[top[x]];
    }
    if(dep[x]>dep[y]) swap(x,y);
    ans^=query(tid[x],tid[y],1);
    return ans;
}

int main(){
    scanf("%d", &n);
    init();
    for(int i=1; i<=n; i++) scanf("%d", &num[i]);
    for(int i=1; i<n; i++){
        int x,y;
        scanf("%d%d",&x,&y);
        addedge(x,y);
        addedge(y,x);
    }
    dfs1(1,1,1);
    dfs2(1,1);
    build(1,n,1);
    for(int i=1; i<=n; i++) update(tid[i],num[i],1);
    scanf("%d", &m);
    while(m--){
        char op[3];
        scanf("%s", op);
        int x, y;
        if(op[0]=='Q'){
            scanf("%d%d", &x,&y);
            int ans=solve(x,y);
            if(!ans) puts("No");
            else puts("Yes");
        }
        else{
            scanf("%d%d",&x,&y);
            update(tid[x],y,1);
        }
    }
    return 0;
}