zoukankan      html  css  js  c++  java
  • 11076: 小P的集合 位运算

      考虑当只有一个数出现奇数次的时候,我们可以很轻松的知道,把所有的数异或和即可,因为异或运算有一个非常有意思的性质,a^b^a=b

     考虑当有两个数(a,b)出现奇数次的时候,我们异或和得到,num=a^b,那么怎么把这两个数分开呢?

     我们想想,既然是位运算,一定和二进制有关,我们把num的二进制展开,我们发现一个问题,这个数的二进制为1的位置,一定是a或者b上的二进制异或留下,

     那么我们可以知道,这位二进制位,要么a是1,要么b是1。

     因此我们枚举这给的数的二进制位,看是否为1,如果为1,如果所有数的异或和在这一位二进制为1,代表对答案位置的二进制可能有贡献(也有可能被异或掉了),我们用cnt[i]代表这个二进制位,代表对i位的二进制影响。

    cnt[i]:异或每一个对这一位有影响的数,他们的异或和只可能有三种张结果。要么是a,b中一个,要是sum(代表包括a,b在内的数对这一位是1都有贡献),或者是0。

     我们再次枚举所有异或和的二进制,如果这一位为1,那么这一位的cnt[i],一定不可能同时存在a,b,但是也一定不可能没有a,b。所以这一位的cnt[i]保存的一定是a,b中的一个。

      

    #include<stdio.h>
    #include<algorithm>
    #include<string.h>
    #include<iostream>
    #define LL long long
    using namespace std;
    LL cnt[40];
    LL n;
    LL k;
    LL sum=0;
    int main(){
      while(~scanf("%lld%lld",&n,&k)){
         LL tmp;
         sum=0;
         for (int i=0;i<n;i++){
            scanf("%lld",&tmp);
            for (int j=0;j<32;j++){
                if((1<<j)&tmp){
                    cnt[j]^=tmp;
                }
            }
            sum^=tmp;
         }
         if (k==1){
            printf("%lld
    ",sum);
            continue;
         }
         LL a=0;
             for (int i=31;i>0;i--){
                if((1<<i)&sum){
                    a=sum^cnt[i];
                    break;
                }
              }
              printf("%lld %lld
    ",a,sum^a);
      }
      return 0;
    }
    有不懂欢迎咨询 QQ:1326487164(添加时记得备注)
  • 相关阅读:
    HDU 4005 The war
    #undef
    [转载] #define new DEBUG_NEW
    [转载]常用正则表达式
    [百科]
    [转载]
    [转载]
    [转载]
    [百科]
    [转载]VC6中的文件后缀
  • 原文地址:https://www.cnblogs.com/bluefly-hrbust/p/10424983.html
Copyright © 2011-2022 走看看