[BZOJ 2819] Nim

Link:https://www.lydsy.com/JudgeOnline/problem.php?id=2819

 

Algorithm:

此题一眼看上去树剖,但事实上双log会TLE

那么此时就要用到异或运算最重要的性质:自反性

 

如果设定一个root,只要计算x和y到root的路径即可,并不要轻重链剖分的计算路径,而这正是因为在LCA(x,y)以上的路径计算了两次,抵消了

但要注意的是,LCA这个点也被计算了两次,因此要再异或LCA这个点的值

 

如果只要计算一个点x到root的路径,那么当修改时只要维护到root的路径上含x的点:即x的子树

当涉及子树的整体操作时,明显可以使用dfs+RMQ的方式区间维护

 

线段树等确实可以用,但实现更方便的树桩数组明显是更好的选择,这就又利用了异或的自反性

初始化:

Update(l[i],dat[i]),Update(r[i]+1,dat[i]);

修改:

Update(l[x],dat[x]);Update(r[x]+1,dat[x]);
Update(l[x],y);Update(r[x]+1,y);
dat[x]=y;

这样查询时只要Query(l[x])即可,充分利用了差分思想,其在异或运算中一样实用

 

Code:

#include <bits/stdc++.h>

using namespace std;

const int MAXN=5e5+10;
int n,m,dat[MAXN],bit[2*MAXN],f[MAXN][20],bin[25],l[MAXN],r[MAXN],dep[MAXN],cnt;
vector<int> G[MAXN];

inline int read()
{
    char ch;int num,f=0;
    while(!isdigit(ch=getchar())) f|=(ch=='-');
    num=ch-'0';
    while(isdigit(ch=getchar())) num=num*10+ch-'0';
    return f?-num:num;
}

void Update(int pos,int val)
{
    while(pos<=n)
    {
        bit[pos]^=val;
        pos+=pos&(-pos);
    }
}

inline int Query(int pos)
{
    int ret=0;
    while(pos)
    {
        ret^=bit[pos];
        pos-=pos&(-pos);
    }
    return ret;
}

void dfs(int x)
{
    for(int i=1;i<20;i++)
        if(dep[x]>=bin[i]) f[x][i]=f[f[x][i-1]][i-1];    
        else break;
    l[x]=++cnt;
    for(int i=0;i<G[x].size();i++)
    {
        int t=G[x][i];
        if(t==f[x][0]) continue;
        dep[t]=dep[x]+1,f[t][0]=x,dfs(t);
    }        
    r[x]=cnt;
}

int LCA(int x,int y)
{
    if(dep[x]<dep[y]) swap(x,y);
    int t=dep[x]-dep[y];
    for(int i=19;i>=0;i--)
        if(t & bin[i]) x=f[x][i];
    for(int i=19;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];
}

int main()
{
    bin[0]=1;for(int i=1;i<=20;i++) bin[i]=bin[i-1]<<1;
    
    n=read();
    for(int i=1;i<=n;i++) dat[i]=read();
    for(int i=1;i<n;i++)
    {
        int x=read(),y=read();
        G[x].push_back(y);G[y].push_back(x);
    }
    dfs(1);
    
    for(int i=1;i<=n;i++)
        Update(l[i],dat[i]),Update(r[i]+1,dat[i]);
    
    m=read();
    for(int i=1;i<=m;i++)
    {
        char op[2];int x,y;
        scanf("%s",&op);x=read();y=read();
        
        if(op[0]=='Q')
        {
            int lca=LCA(x,y);
            int res=Query(l[x])^Query(l[y])^dat[lca];  //Core
            if(res) puts("Yes");
            else puts("No");
        }
        else
        {
            Update(l[x],dat[x]);Update(r[x]+1,dat[x]);  //Update
            Update(l[x],y);Update(r[x]+1,y);
            dat[x]=y;
        }
    }
    return 0;
}

 

Review:

1、出现异或时,考虑自反性与差分法

 

2、对子树整体操作,dfs处理出l[i],r[i],从而优化

 

3、1e5以上的数据都要读写优化(或puts)

posted @ 2018-05-17 15:34  NewErA  阅读(121)  评论(0编辑  收藏  举报