bzoj2819 Nim

题意:给定一棵带点权的树,每次询问用一条路径上的点玩Nim游戏先手是否必胜,支持单点修改。

Nim游戏:所有堆的数目异或起来不为0时先手必胜,否则必败.

所以就是单点修改+路径异或和查询. 树剖一发,因为异或满足区间减法所以可以树剖套树状数组。(还有人说可以套zkw线段树?)

Dzy大爷说了一种dfs序+树状数组的方法:

 http://dzy493941464.is-programmer.com/posts/40428.html

既然它只修改点的话,影响到的只是它这棵子树。那么很容易就想到了dfs序。这个子树就是连续一段。

先维护每个点dfs开始时和结束时的时间戳。修改的时候先在它自己的开始、结束位置上xor它自己变成零,然后再修改。

(x,y)路径上的xor值=query(x的开始) xor query(y的开始) xor lca(x,y)的点权。很好想通。LCA就倍增算一下好了。

没了。

“很好想通”,表示蒟蒻脑子有坑想了好久才明白QAQ。

首先,在求树上点对距离的时候我们是用节点到根的距离减去lca到根的距离,比如u到v的距离等于dis[u]+dis[v]-2*dis[lca(u,v)]

这里把点i到根节点路径上点的异或和记作sum[i],i的点权记作w[i],那么u,v路径上的异或和就是sum[u]^sum[v]^w[lca(u,v)]

这么做的原因在于lca(u,v)到根节点路径上的点权值在sum[u]和sum[v]中都出现,异或一下就没了.但是lca(u,v)的点权应当在答案中出现,却也被消掉了,所以还要单独异或上。

Lca可以倍增一发,那么问题还剩下动态维护每个点到根节点路径上的异或和.转换思路,分别考虑每个点能够影响其他哪些点的sum值,对于每个点i将它能影响的所有点的sum值进异或上w[i]。这个操作可以高效地完成,因为每个点只会影响它所在子树内的点的sum值。而一棵子树内的点在dfs序中是连续的一段,所以我们只需要一个数据结构支持区间修改单点查询,那么就可以树状数组了。查询sum[u]的时候直接查询u的DFS序在树状数组中对应的位置即可。

注意会卡爆栈,所以需要用BFS求DFS序。

#include<cstdio>
const int maxn=500005;
struct edge{
    int to,next;
}lst[maxn<<1];int len=1;
int first[maxn];
void addedge(int a,int b){
    lst[len].to=b;
    lst[len].next=first[a];
    first[a]=len++;
}
int w[maxn],pos[maxn],prt[maxn],depth[maxn],hvy[maxn],top[maxn],sz[maxn];
int q[maxn],head,tail;
int c[maxn];
inline int lowbit(int x){
    return x&(-x);
}
void add(int x,int w){
    for(;x<maxn;x+=lowbit(x)){
        c[x]^=w;
    }
}
int sum(int x){
    int ans=0;
    for(;x;x-=lowbit(x))ans^=c[x];
    return ans;
}
void bfs1(){
    head=tail=0;
    q[tail++]=1;depth[1]=1;
    while(head!=tail){
        int x=q[head++];
        sz[x]=1;
        for(int pt=first[x];pt;pt=lst[pt].next){
            if(lst[pt].to==prt[x])continue;
            prt[lst[pt].to]=x;
            depth[lst[pt].to]=depth[x]+1;
            q[tail++]=lst[pt].to;
        }
    }
    for(int i=tail-1;i>=0;--i){
        int x=q[i];
        sz[prt[x]]+=sz[x];
        if(sz[x]>sz[hvy[prt[x]]])hvy[prt[x]]=x;
    }
}
void bfs2(){
    top[1]=1;pos[1]=1;
    for(int i=0;i<tail;++i){
        int x=q[i];
        add(pos[x],w[x]);
        if(hvy[x]){
            top[hvy[x]]=top[x];
            pos[hvy[x]]=pos[x]+1;
            int cntsz=sz[hvy[x]];
            for(int pt=first[x];pt;pt=lst[pt].next){
                if(lst[pt].to==prt[x]||lst[pt].to==hvy[x])continue;
                top[lst[pt].to]=lst[pt].to;
                pos[lst[pt].to]=pos[x]+cntsz+1;
                cntsz+=sz[lst[pt].to];
            }
        }
    }
}
inline void swap(int &a,int &b){
    int tmp=a;a=b;b=tmp;
}
int query(int u,int v){
    int ans=0;
    int t1=top[u],t2=top[v];
    while(t1!=t2){
        if(depth[t1]<depth[t2])swap(t1,t2),swap(u,v);
        ans^=sum(pos[t1]-1);
        ans^=sum(pos[u]);
        u=prt[t1];t1=top[u];
    }
    if(depth[u]>depth[v])swap(u,v);
    ans^=sum(pos[u]-1);ans^=sum(pos[v]);
    return ans;
}
int main(){
    int n;scanf("%d",&n);
    for(int i=1;i<=n;++i)scanf("%d",w+i);
    int a,b;
    for(int i=1;i<n;++i){
        scanf("%d%d",&a,&b);
        addedge(a,b);addedge(b,a);
    }
    bfs1();
    bfs2();
    int m;scanf("%d",&m);
    char buf[10];
    while(m--){
        scanf("%s%d%d",buf,&a,&b);
        if(buf[0]=='Q'){
            if(query(a,b)==0)printf("No\n");
            else printf("Yes\n");
        }else{
            add(pos[a],w[a]);
            w[a]=b;
            add(pos[a],w[a]);
        }
    }
    return 0;
}
#include<cstdio>
const int maxn=500005;
struct edge{
    int to,next;
}lst[maxn<<1];int len=1;
int first[maxn];
void addedge(int a,int b){
    lst[len].to=b;
    lst[len].next=first[a];
    first[a]=len++;
}
int w[maxn];
int c[maxn];
inline int lowbit(int x){
    return x&(-x);
} 
void add(int x,int w){
    for(;x<maxn;x+=lowbit(x))c[x]^=w;
}
int query(int x){
    int ans=0;
    for(;x;x-=lowbit(x))ans^=c[x];
    return ans;
}
int q[maxn];
int dfn[maxn],sz[maxn],depth[maxn];
int p[maxn][20];
void bfs(){//求dfs序和倍增的预处理都在这里了 
    int head=0,tail=0,x,cntsz;
    q[tail++]=1;depth[1]=1;
    while(head!=tail){
        x=q[head++];
        for(int pt=first[x];pt;pt=lst[pt].next){
            if(lst[pt].to==p[x][0])continue;
            p[lst[pt].to][0]=x;
            depth[lst[pt].to]=depth[x]+1;
            q[tail++]=lst[pt].to;
        }
        for(int j=0;p[x][j];++j)p[x][j+1]=p[p[x][j]][j];
    }
    for(int i=tail-1;i>=0;--i){
        x=q[i];
        sz[x]++;
        sz[p[x][0]]+=sz[x];
    }
    dfn[1]=1;
    for(int i=0;i<tail;++i){
        x=q[i];cntsz=0;
        for(int pt=first[x];pt;pt=lst[pt].next){
            if(lst[pt].to==p[x][0])continue;
            dfn[lst[pt].to]=dfn[x]+cntsz+1;
            cntsz+=sz[lst[pt].to];
        }
    }
}
inline void swap(int &a,int &b){
    int tmp=a;a=b;b=tmp;
}
int lca(int u,int v){
    if(depth[u]<depth[v]){
        swap(u,v);
    }
    for(int j=19;j>=0;--j){
        if(depth[p[u][j]]>=depth[v])u=p[u][j];
    }
    if(u==v)return u;
    for(int j=19;j>=0;--j){
        if(p[u][j]!=p[v][j]){
            u=p[u][j];v=p[v][j];
        }
    }
    return p[u][0];
}

int main(){
    int n;scanf("%d",&n);
    for(int i=1;i<=n;++i)scanf("%d",w+i);
    int a,b;
    for(int i=1;i<n;++i){
        scanf("%d%d",&a,&b);
        addedge(a,b);addedge(b,a);
    }
    bfs();
    for(int i=1;i<=n;++i){
        add(dfn[i],w[i]);add(dfn[i]+sz[i],w[i]);
    } 
    int q;scanf("%d",&q);
    char buf[10];
    int tmp;
    while(q--){
        scanf("%s%d%d",buf,&a,&b);
        if(buf[0]=='Q'){
            tmp=query(dfn[a])^query(dfn[b])^w[lca(a,b)];
            if(tmp){
                printf("Yes\n");
            }else{
                printf("No\n");
            }    
        }else{
            add(dfn[a],w[a]);add(dfn[a]+sz[a],w[a]);
            w[a]=b;
            add(dfn[a],w[a]);add(dfn[a]+sz[a],w[a]);
        }
    }
    return 0;
}

 

posted @ 2016-10-27 21:23  liu_runda  阅读(197)  评论(0编辑  收藏  举报
偶然想到可以用这样的字体藏一点想说的话,可是并没有什么想说的. 现在有了:文化课好难