题意:
求 \((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;
}