• [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)

  • 相关阅读:
    disable_irq与disable_irq_nosync使用场景
    linux中断处理原理分析
    工作队列(workqueue) create_workqueue/schedule_work/queue_work
    使用git建立远程仓库,让别人git clone下来
    C中字符串的几种定义方法及说明
    Linux 2.6内核Makefile浅析
    探究platform_driver中的shutdown用途
    匆匆
    至强CPU性能排行,从X3210起,由低至高排列。
    Linux 命令行快捷键
  • 原文地址:https://www.cnblogs.com/newera/p/9051407.html
Copyright © 2020-2023  润新知