题目链接
小P最近对集合非常感兴趣,他看到了这样一个问题:
在集合中找出 (k≤2)个出现了奇数次的正整数 a。
保证所有数据正好有 k个数出现了奇数次。
但是小P的电脑性能非常差,可用内存非常少,只有4MB,所以他想请你帮他写一个程序解决这个问题!
题解:
【小结】:
比赛的时候的确是没有想到,但是我就差一步了。。。就差一步,我已经知道k=1的时候是异或,k=2的时候异或和,至少有一位是1,然后怎么根据这个位置上的1分开为两个呢?我就不懂了。赛后一看题解,就一直骂自己傻逼,就差一步了,我就没想到。
【题解】:
对于这个题目卡内存,就是说根本不能开数组进行存储,一定需要找规律,我们在以前hdu新生题里面就有一题是送礼物的,然后有一个特别的为奇数,然后让我们找出来,所以说,k=1就直接异或和即可。
当k=2时,至少有一位是1。
然后用开数组模拟每一次输入的X,转化为2进制后,最大只有31位。
开一个cnt[32]的数组。
只要有转化后只要对应位置有1的,把cnt[i]^=x。
为什么进行这个操作,大家想想,k=2时,其余位置肯定都是偶数对,我们转化为二进制的 i位置上,即
然后对于每一个为1的位置都进行异或,相当于对于i位置有x的出现过。
输入n个x后,
最后就会转化为,怎么通过求a,b呢??
从高位枚举,我们知道至少有一位是1,那么我们a=c^cnt[i],那么b=cnt[i]
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=40;
ll cnt[N];
ll n,k,x,sum;
int main()
{
scanf("%lld%lld",&n,&k);
for(int i=1;i<=n;i++){
scanf("%lld",&x);
for(int j=31;j>=0;j--){
if((1<<j)&x){
cnt[j]^=x;
}
}
sum^=x;
}
ll a,b;
if(k==1){
return 0*printf("%lld\n",sum);
}
for(int i=31;i>=0;i--){
if((1<<i)&sum){
a=sum^cnt[i];
b=cnt[i];
return 0*printf("%lld %lld\n",a,b);
}
}
}