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; }
有不懂欢迎咨询
QQ:1326487164(添加时记得备注)