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; }
朋友们,无论这个世界变得怎样,只要能够为了当时纯粹的梦想和感动坚持努力下去,不管其它人怎么样,我们也能够保持自己的本色走下去。