CodeForces 1322B - Present【二进制】

题意:

求 \((a_1+a_2)\bigoplus(a_1+a_3)\bigoplus ... \bigoplus(a_{n-1}+a_n)\)
数据范围:\(2\leq n \leq 4*10^5 ,1\leq a_i \leq 10^7\)

分析:

对答案的每一位二进制位单独考虑。
  对于答案的第 \(k\) 位,如果要使其为 \(1\) ,那么必然有奇数个第 \(k\) 位为 \(1\) 的 \((a_i+a_j)\) 进行异或。对于每个数,第 \(k\) 位之前的位置的数并不会影响该结果,因此可以让每个数对 \(2^{(k+1)}\) 进行取模,使每个数处于 \([0,2^{(k+1)}-1]\) 内,则\((a_i+a_j)\in [0,2^{(k+2)}-2]\)。要使 \((a_i+a_j)\) 的第 \(k\) 位为 \(1\) ,则其取值范围为 \([2^k,2^{(k+1)}-1]\) 或者 \([2^{(k+1)}+2^k,2^{(k+2)}-2]\)。可以对取模后的数进行排序,枚举每个数,然后二分求出有多少个数和它相加的和在上述的两个区间内。如果个数为奇数个,即可以异或到答案中。
二分时,要在当前枚举数前面进行寻找。
以后看到和位运算有关的题目,可以往二进制方面多想想。

复杂度:\(O(n*logn*logn)\)

代码:

#include <bits/stdc++.h>
using namespace std;
const int N=4e5+5;
int a[N],b[N];
int main()
{
    int n,L,R,t,ans=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int k=0;k<=24;k++)//枚举答案的二进制位
    {
        int mod=1<<(k+1);//高位不影响
        for(int i=1;i<=n;i++)
            b[i]=a[i]%mod;//[0,2^(k+1)-1]
        sort(b+1,b+1+n);
        for(int i=1;i<=n;i++)
        {
            L=max(0,(1<<k)-b[i]);R=(1<<(k+1))-1-b[i];
            t=upper_bound(b+1,b+i,R)-lower_bound(b+1,b+i,L);
            if(t&1)
                ans^=(1<<k);
            L=max(0,(1<<(k+1))+(1<<k)-b[i]);R=(1<<(k+2))-2-b[i];
            t=upper_bound(b+1,b+i,R)-lower_bound(b+1,b+i,L);
            if(t&1)
                ans^=(1<<k);
        }
    }
    printf("%d\n",ans);
    return 0;
}
上一篇:关于状压DP枚举子集的方法与理解


下一篇:洛谷 P5614题解