• 【整理】XOR:从陌生到头晕


     一:解决XOR常用的方法:

     在vjudge上面输入关键词xor,然后按照顺序刷了一些题。

    然后大概悟出了一些的的套路:

    1.    常用的有贪心,主要是利用二进制的一些性质,即贪心最大值的尽量高位取1。
    2.    然后有前缀异或和,和普通前缀和一样,可以快速得到一段区间的异或和。
    3.    当然在一颗树里面也常用前缀异或和,得到根节点到每个节点的前缀异或和,然后,两个点的前缀异或和在异或,可以得到两个点之间路线的异或和。     因为LCA到根的公共部分可以抵消(感谢Lzh提醒)。
    4.    Trie树,可以快速在数组里找自己的最大异或。
    5.    这种题,当然少不了分块,随机应变吧。

                                     -------------------------------我是分界线-----------------------------------

    题意:给出一棵树,在树上找出一条路径,使得路径伤的边的异或值最大。
    思路:dfs得到根到节点的异或前缀和,然后把每个点的异或前缀和插入字典树中,就可以按套路,在字典树里找最大异或了。
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int maxm=200010;
    const int maxn=3000000;
    int Laxt[maxm],Next[maxm],To[maxm],val[maxm],cnt,Xor[maxm];//dfs
    int ch[maxn][2],tot,ans,b[40],n;//trie
    int q_pow(int a,int x){ int res=1;while(x){if(x&1) res*=a;x>>=1;a*=a;} return res;}
    int read()
    {
        int res=0; char c=getchar();
        for(;c>'9'||c<'0';c=getchar());
        for(;c<='9'&&c>='0';res=res*10+c-'0',c=getchar()) ;
        return res;
    }
    void init()
    {
        memset(Laxt,0,sizeof(Laxt));
        memset(Xor,0,sizeof(Xor));
        memset(ch,0,sizeof(ch));
        cnt=tot=ans=0;
    }
    void add(int u,int v,int x)
    {
        Next[++cnt]=Laxt[u];
        Laxt[u]=cnt;
        To[cnt]=v;
        val[cnt]=x;
    }
    void dfs(int u,int pre,int x)
    {
        for(int i=Laxt[u];i;i=Next[i]){
            if(To[i]!=pre){
                Xor[To[i]]=x^val[i];
                dfs(To[i],u,Xor[To[i]]);
            }
        }
    }
    void insert(int x)
    {
        int Now=0; 
        for(int i=0;i<=31;i++) { b[i]=x&1;x>>=1;}
        for(int i=31;i>=0;i--){
            if(!ch[Now][b[i]]) ch[Now][b[i]]=++tot;
            Now=ch[Now][b[i]];
        }
    }
    void find(int x)
    {
        int Now=0,tmp=0;
        for(int i=0;i<=31;i++){ b[i]=x&1; x>>=1; }
        for(int i=31;i>=0;i--){
            if(ch[Now][b[i]^1]) Now=ch[Now][b[i]^1],tmp+=q_pow(2,i);
            else Now=ch[Now][b[i]];
        } ans=max(ans,tmp);
    }
    void build()
    {
        for(int i=1;i<=n;i++) insert(Xor[i]);
        for(int i=1;i<=n;i++) find(Xor[i]);
    }
    int main()
    {
        while(~scanf("%d",&n)){
            init();  int u,v,x;
            for(int i=1;i<n;i++){        
                u=read();v=read();x=read();    
                u++;v++;
                add(u,v,x); add(v,u,x);
            }
            dfs(1,0,0); build();
            printf("%d
    ",ans);
        } return 0;
    }
    View Code
     题意: 给定L,R。求L<=X1,X2,X3...<=R,使得X1^X2^X3...最大异或。
     思路: 可以选的数大于大于两个,先求最大的n,使得2^n<=R, 如果还可以异或一个,那么异或(2^n)-1就好了。(2^n)xor(2^n-1) =2^(n+1)-1
    一定是最大的。比如2
    ^=10000, n=410000 xor 01111 = 11111;不可能还有不这个大的了,毕竟n=4是上界; 当然只能选一个的时候,
    就选本身R就好了。当然,为了避免卡精度问题(比如CF就hack我了,mmp),pow函数最好比较一下,这里太懒,算了。
    #include<cstdio>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    int main()
    {
        int a,b,ans,L,R;
        while(~scanf("%d%d",&L,&R)){
            if(L==R) printf("%d
    ",L);
            else {
                int a=log2(R);
                a=q_pow(2,a);
                printf("%d
    ",a+a-1);
            }
        } return 0;
    }
    View Code
    •     HDU 4825:Xor Sum  字典树

    题意: 给出n个数a[],然后给出m次问题,给出Y,求a[]里面的X,使得X xor Y最大。
    思路: 如果找一个数的最大异或,当然我们需要从高位到低位(已转化为二进制),尽可能不同。
          那么我们从高位到低位表示一个数X,并且存入字典树中,结尾节点记录X(不同的数结尾肯定不同)。
          对于询问,从高位到地位先尽可能从兄弟边走。
          这棵Trie树高度为32,复杂度在接受范围内。
    #include<cstdio>
    #include<cstdlib>
    #include<iostream>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    const int maxn=3000010;
    inline int read()
    {
        int res=0;char x=getchar();while(x<'0'||x>'9') x=getchar();
        while(x>='0'&&x<='9'){ res=res*10+x-'0';x=getchar();}return res;
    }
    int q_pow(int a,int x){ int res=1;while(x){ if(x&1) res*=a; x>>=1; a=a*a;} return res; }
    struct TREE
    {
        int ch[maxn][2],cnt,num[maxn];
        void init() { memset(ch,0,sizeof(ch));cnt=0;}
        void insert(int s[],int val)
        {
            int Now=0;
            for(int i=31;i>=0;i--){
                if(!ch[Now][s[i]]) ch[Now][s[i]]=++cnt;
                Now=ch[Now][s[i]];
            }   num[Now]=val;
        }
        int query(int s[])
        {
            int Now=0;
            for(int i=31;i>=0;i--){
                if(ch[Now][s[i]^1]) Now=ch[Now][s[i]^1];
                else Now=ch[Now][s[i]];
            }   return num[Now];
        }
    }Tree;
    int main()
    {
        int T,n,m,x,tx,a[32],Case=0;
        scanf("%d",&T);
        while(T--){
            printf("Case #%d:
    ",++Case);
            scanf("%d%d",&n,&m);
            Tree.init();
            for(int i=1;i<=n;i++){
                 scanf("%d",&x);tx=x;
                 for(int j=0;j<=31;j++) {
                     a[j]=x%2; x>>=1;
                 } Tree.insert(a,tx);
            }
            for(int i=1;i<=m;i++) {
                 scanf("%d",&x);
                 for(int j=0;j<=31;j++) {
                     a[j]=x%2; x>>=1;
                 } printf("%d
    ",Tree.query(a));
            } 
        }return 0;
    }
    View Code

              (和上一题差不多,就不说了)

    题意:  给定数列a[],和m个询问 Q(L,R),回答每个询问中有多少对(L<=i<=j<=R) ,使得异或为k。
    思路:  异或转化为前缀和处理。然后就差不多交给分块处理了。 分块的时候记录个数,记录区间信息,具体的代码里面去感受。
    #include<cstdio>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #define ll long long
    using namespace std;
    const int maxn=100010;
    int a[maxn],pre[maxn],num[1<<20],n,m,k,sqrtn;
    struct Query{
        int id,l,r;  ll ans;
    }q[maxn];
    bool cmp(const Query a,const Query b)
    {
        if(a.l/sqrtn==b.l/sqrtn) return a.r<b.r;
        return a.l<b.l;
    }
    bool cmp2(const Query a,const Query b)
    {
        return a.id<b.id;
    }
    int main()
    {
         scanf("%d%d%d",&n,&m,&k);
         sqrtn=(int)sqrt(n);
         for(int i=1;i<=n;i++) {
              scanf("%d",&a[i]);
              pre[i]=pre[i-1]^a[i];
         }
         for(int i=0;i<m;i++){
             scanf("%d%d",&q[i].l,&q[i].r);
             q[i].id=i;
         }
         sort(q,q+m,cmp);
         int l=1,r=1;
         num[pre[1]]++;num[0]++;
         ll cur=(a[1]==k?1:0);
         for(int i=0;i<m;i++)
         {
            while(r<q[i].r){
                cur+=num[pre[r+1]^k];
                r++;
                num[pre[r]]++;
            }
            while(l<q[i].l){
                num[pre[l-1]]--;
                cur-=num[pre[l-1]^k];
                l++;
            }
            while(l>q[i].l){
                cur+=num[pre[l-2]^k];
                num[pre[l-2]]++;
                l--;
            }
            while(r>q[i].r){
                num[pre[r]]--;
                cur-=num[pre[r]^k];
                r--;
            }
            q[i].ans=cur;
         }
         sort(q,q+m,cmp2);
         for(int i=0;i<m;i++) printf("%lld
    ",q[i].ans);
         return 0;
    }
    View Code
    题意:现在对对于这个题,求a<=X<=b,c<=Y<=d,使XxorY最大(不同位数最多)。 
    思路:从高位向地位,能取不同则取不同。
    #include<cstdio>
    #include<cstdlib>
    #include<iostream>
    #include<cmath>
    using namespace std;
    long long  a,b,c,d,x,y,t1,t2,ans;
    int main()
    {
        long long i,j,T;
        scanf("%lld",&T);
        while(T--){
            ans=x=y=0;
            scanf("%lld%lld%lld%lld",&a,&b,&c,&d);
            for(i=62;i>=0;i--){
                 t1=x+(1LL<<i);t2=y+(1LL<<i);
                 if(t1<=b&&t2-1>=c) {
                        x=t1;ans+=(1LL<<i);
                 }
                 else if(t2<=d&&t1-1>=a){
                        y=t2;ans+=(1LL<<i);
                 }
                 else if(t1-1>=a&&t2-1>=c){
                        continue;
                 }
                 else if(t1<=b&&t2<=d){
                        x=t1;y=t2;
                 }
            }
            printf("%lld
    ",ans);
        }
        return 0;
    }
     
    View Code
    问题:  有2*n-1个袜子,叫你找出不能配对的那个袜子。
    #include<cstdio>
    #include<cstdlib>
    #include<iostream>
    #include<cstring>
    #include<memory.h>
    using namespace std;
    char s[10],c;
    int main()
    {
        int n;
        while(~scanf("%d
    ",&n)){
            for(int i=0;i<=7;i++) s[i]='';
            for(int i=1;i<2*n;i++){
                for(int j=0;j<8;j++){
                    c=getchar();
                    s[j]=s[j]^c;
                }
            }
            printf("%s",s);
        }
        return 0;
    }
    View Code
    • NBUT1615:XorXor 异或的性质

    问题:求所有区间异或和的异或和。
    思路:由于异或满足交换律,偶数次异或的数字可以怼掉。所以只需要求出每个数字是出现奇数次还是偶数次即可。
    #include<cmath>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    int main()
    {
        int T,n,i,x,ans;
        scanf("%d",&T);
        while(T--){
            scanf("%d",&n); ans=0;
            for(i=1;i<=n;i++){
                scanf("%d",&x);
                if((i*(n+1-i))&1) ans^=x;
            } printf("%d
    ",ans);
        } return 0;
    }
    View Code
    • CodrChef:Bear and Xor of Sums 

    题意:问所有的区间和的异或和。
    思路:和上一题不同,这里有加法。
  • 相关阅读:
    如何克服晕车?
    简单C程序,迷宫
    非名校毕业年薪20W程序员 心得分享
    北京:一个大学生每年要花多少钱
    PowerPoint超链接字体颜色修改、怎么去掉超链接下划线
    河南旅游景点介绍
    程序员必看的十大电影
    AMD的cpu如何安装Mac OS
    C/C++面试之算法系列--去除数组中的重复数字
    Warshall算法
  • 原文地址:https://www.cnblogs.com/hua-dong/p/8244954.html
Copyright © 2020-2023  润新知