【BZOJ】2819:Nim-树剖&线段树&手写栈

传送门:bzoj2819


题解

题中的Nim游戏其实就是看选中的这几堆石头每堆石头数量的异或和是否为0。
我们只需要维护每个点到根节点路径上的异或和就好了。
每次询问只要把两个节点到根节点路径异或和异或起来(抵消了LCA-ROOT一段)再异或一下LCA就好了。
对于修改,考虑只会影响到该节点为根的子树内的所有节点。直接dfs序建个线段树改一下。
被题面坑了,写了一个手写栈。


代码

#include<bits/stdc++.h>
using namespace std;
const int M=1e7+10;
const int N=5e5+10;
int n,m,T,f[N][19];
int Q[M],top,a[N],b[N],d[N],ans;
int mv[N<<2],orz[N<<2],bin[20],ori[N];
int head[N],to[N<<1],nxt[N<<1],tot;
int df[N],ot[N],in[N],cnt,as[N];
int opt;

inline int rd(){
    char ch;int x=0,f=1;
    while(!isdigit(ch=getchar())){if(ch=='-') f=0;}
    x=(ch^48);
    while(isdigit(ch=getchar())){x=(x<<3)+(x<<1)+(ch^48);}
    return f?x:-x;
}

inline void lk(int u,int v)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;}

inline int get()
{
    char ch=getchar();
    while(ch!='Q' && ch!='C') ch=getchar();
    return ch=='Q'? 0:1;
}

inline void bfs()
{
    int x,ptr;Q[++top]=1;as[1]=a[1];
    while(top){
        x=Q[top];if(!df[x]) df[x]=++cnt;
        for(int i=1;bin[i]<=d[x];i++)
        f[x][i]=f[f[x][i-1]][i-1];ptr=1;
        for(int &i=ori[x];i;i=nxt[i]){
            if(to[i]==f[x][0]) continue;
            f[to[i]][0]=x;d[to[i]]=d[x]+1;
            as[to[i]]=as[x]^a[to[i]];
            if(!in[to[i]]){
               Q[++top]=to[i];ptr=0;break;
            }
        }
        if(ptr){in[x]=1;top--;if(!ot[x]) ot[x]=cnt;}
    }
}

inline int LCA(int x,int y)
{
    if(d[x]<d[y]) swap(x,y);
    int t=d[x]-d[y];
    for(int i=0;bin[i]<=t;i++)
     if(bin[i]&t) x=f[x][i];
    for(int i=18;i>=0;i--){
        if(f[x][i]!=f[y][i])
         x=f[x][i],y=f[y][i];
    }
    if(x==y) return x;
    return f[x][0];
}

inline void pushdown(int k)
{ 
  if(orz[k]){
    mv[k<<1]^=orz[k];orz[k<<1]^=orz[k];
    mv[k<<1|1]^=orz[k];orz[k<<1|1]^=orz[k];
    orz[k]=0;
  }
}

inline void add(int k,int l,int r,int L,int R,int val)
{
    if(l!=r)pushdown(k);
    if(l>=L && r<=R){mv[k]^=val;orz[k]=val;return;}
    int mid=(l+r)>>1;
    if(L<=mid) add(k<<1,l,mid,L,R,val);
    if(R>mid) add(k<<1|1,mid+1,r,L,R,val);
}

inline int qr(int k,int l,int r,int pos)
{
    if(l==r) return mv[k];
    pushdown(k);
    int mid=(l+r)>>1;
    if(pos<=mid) return qr(k<<1,l,mid,pos);
    else return qr(k<<1|1,mid+1,r,pos);
}

inline void cg(int v,int k)
{
    int cgd=b[v]^k;b[v]=k;if(!cgd) return;
    add(1,1,n,df[v],ot[v],cgd);
}

inline void query(int x,int y)
{
    int t=LCA(x,y);
    int X=as[x]^qr(1,1,n,df[x]),Y=as[y]^qr(1,1,n,df[y]);
    ans=X^Y^b[t];
    if(!ans) printf("No\n");
    else printf("Yes\n");
}

int main(){
    bin[0]=1;for(int i=1;i<20;i++) bin[i]=bin[i-1]<<1;
    n=rd();
    for(int i=1;i<=n;i++) b[i]=a[i]=rd();
    for(int u,v,i=1;i<n;i++){
        u=rd();v=rd();lk(u,v);lk(v,u);
    }
    for(int i=1;i<=n;i++) ori[i]=head[i];
    bfs();
    T=rd();int cur=0;
    while(T--){
       opt=get();int U=rd(),V=rd();
       if(!opt) query(U,V);
       else cg(U,V);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/corsica6/article/details/80349580