Codeforces Round #626 Div2 D. Present(位掩码,二分)

题目链接:https://codeforces.com/contest/1323/problem/D

题意:给了大小为4e5的数组a,其中1<=ai<=1e7。求所有点对和的异或和,即:

 

 


 

 

思路:

  • 按位来考虑,因为两个元素的和<=2e7,而2e7小于225,因此结果最多是25位。我们考虑答案中每一位ans[k]的值(0<=k<=24)。
  • 显然,ai中仅有0~k位会对第k位有影响,因此对数组a全部对2k+1取模,得到数组b,对b按升序排序。
  • 这样,0<=bi<=2k+1-1,两个bi的和<=2k+2-2。要使得和的第k位为1,和的范围应该为[ 2k , 2k+1-1 ] (区间1)或者 [ 2k+2k+1 , 2k+2-2] (区间2)。
  • 枚举bi,对两个区间可分别得到另一个和bi相加的元素bj的范围大小,利用二分查找bj的区间,从而得到bj的个数num。枚举完之后num%2=1的话,答案中的第k位便是1。
  • 另外,如果bi<=2k,那么bi和另一个元素的和只可能在区间1内,否则既可以在区间1,也可以在区间2。

AC code:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int maxn=4e5+5;
typedef long long LL;
int n,a[maxn],b[maxn],p[30],ans,MOD;

void init(){
    p[0]=1;
    for(int i=1;i<=26;++i)
        p[i]=p[i-1]<<1;
}

int bs1(int ll,int rr,int v){
    int l=ll,r=rr,mid;
    while(l<=r){
        mid=(l+r)>>1;
        if(b[mid]>=v) r=mid-1;
        else l=mid+1;
    }
    return l;
}

int bs2(int ll,int rr,int v){
    int l=ll,r=rr,mid;
    while(l<=r){
        mid=(l+r)>>1;
        if(b[mid]<=v) l=mid+1;
        else r=mid-1;
    }
    return r;
}

int main(){
    init();
    scanf("%d",&n);
    for(int i=1;i<=n;++i)
        scanf("%d",&a[i]);
    for(int k=0;k<=24;++k){
        LL num=0;
        MOD=p[k+1];
        for(int i=1;i<=n;++i)
            b[i]=a[i]%MOD;
        sort(b+1,b+n+1);
        for(int i=1;i<n;++i){
            int le=max(p[k]-b[i],0),ri=p[k+1]-1-b[i];
            int l=bs1(i+1,n,le);
            int r=bs2(i+1,n,ri);
            num+=(LL)(r-l+1);
            if(b[i]<=p[k]) continue;
            le=p[k+1]+p[k]-b[i],ri=p[k+2]-2-b[i];
            l=bs1(i+1,n,le);
            r=bs2(i+1,n,ri);
            num+=(LL)(r-l+1);
        }
        if((int)(num%2)==1) ans+=p[k];
    }
    printf("%d\n",ans);
    return 0;
}

 

posted @ 2020-03-08 13:18  Frank__Chen  阅读(281)  评论(0编辑  收藏  举报