【异或】小P的集合

题目链接

小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位置上,即

\large x=\sum_{i=0}^{31}2^i

然后对于每一个为1的位置都进行异或,相当于对于i位置有x的出现过。

输入n个x后,

最后就会转化为\large a \wedge b=c,怎么通过求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);
        }
    }

}

猜你喜欢

转载自blog.csdn.net/Z_sea/article/details/87822613