题意:
给长度为n的数组a和b
输出(a(1)+b(1))异或(a(1)+b(2))异或…(a(1)+b(n))异或(a(2)+b(1))异或…(a(2)+b(n))异或…(a(n)+b(n))的值
即从a中任意选择一个数a(i),b中任意选择一个数b(j),所有情况组成的a(i)+b(j)的异或和
数据范围:n<=2e5,0<=a(i),b(i)<=228
思路:
暴力显然超时。
看到异或很容易想到按位计算贡献
但是有加法很麻烦,因为加法可能会产生进位,怎么处理加法进位不是很好想。
假设我们当前要计算第x位的贡献,那么显然高于x的位对第x位是否为1不会产生影响,只考虑二进制的1到x位。
设T=2x,将a数组和b数组对2T取模,则得到低x位的值。
要使得a(i)+b(j)的第x位为1,则需满足以下两个条件之一:
T<=a(i)+b(j)<2T,(第x位不发生进位,此条件为第x位为1,x位下面任意)
3T<=a(i)+b(j)<4T,(第x位发生进位,此条件位第x和第x+1位都为1,x位下面任意)
因此可以枚举a(i),分别两次二分出满足这两种条件的区间端点,区间长度即为第x位为1的数对个数,
如果数对总和是奇数个则当前位对答案有贡献。
ps:
二分可以用lower_bound,码量会小很多(只是我不习惯用)。
code:
#include<bits/stdc++.h>
using namespace std;
const int maxm=2e5+5;
int a[maxm];
int b[maxm];
int c[maxm];
int d[maxm];
int n;
int ask(int left,int right){//计算d数组中left<=x<right的x的数量
int st=0,ed=0;
int l=1,r=n;
while(l<=r){//st尽量向左扩展
int mid=(l+r)/2;
if(d[mid]>=left){
st=mid;
r=mid-1;
}else{
l=mid+1;
}
}
l=1,r=n;
while(l<=r){//ed尽量向右扩展
int mid=(l+r)/2;
if(d[mid]<right){
ed=mid;
l=mid+1;
}else{
r=mid-1;
}
}
if(!st||!ed)return 0;//如果不能同时满足x>=left且x<right,则无解
return ed-st+1;
}
signed main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++){
cin>>b[i];
}
int ans=0;
for(int i=0;i<30;i++){
int t=(1<<i);
for(int j=1;j<=n;j++){
c[j]=a[j]%(t*2);
d[j]=b[j]%(t*2);
}
sort(c+1,c+1+n);
sort(d+1,d+1+n);
int cnt=0;
for(int j=1;j<=n;j++){//枚举c
cnt+=ask(t-c[j],t*2-c[j]);
cnt+=ask(t*3-c[j],t*4-c[j]);
cnt%=2;
}
if(cnt){
ans+=(1<<i);
}
}
cout<<ans<<endl;
return 0;
}