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\n",sum);
        continue;
     }
     LL a=0;
         for (int i=31;i>0;i--){
            if((1<<i)&sum){
                a=sum^cnt[i];
                break;
            }
          }
          printf("%lld %lld\n",a,sum^a);
  }
  return 0;
}

 

posted @ 2019-02-23 23:57  bluefly-hrbust  阅读(175)  评论(0编辑  收藏  举报