[关闭]
@xiaoziyao 2020-10-17T09:45:33.000000Z 字数 1547 阅读 1042

CF665E Beautiful Subarrays解题报告

解题报告


CF665E Beautiful Subarrays解题报告:

更好的阅读体验

题意

分析

自己想出了做法,鸡冻。

首先根据异或的性质,求出异或的前缀和,把异或和转化为有多少个数对满足异或起来大于等于(其中数对需要在异或前缀和数组中)。

然后我们对异或前缀和数组建一颗,并求出每个点子树中终止点的个数(终止点就是插入时最后一个点),查询的时候按位查询。

具体地,我们对于每一位分类讨论,设第,第),假如我们前位与都相同:

  1. long long calc(int x,int k){
  2. int now=root;
  3. long long res=0;
  4. for(int i=30;i>=0;i--){
  5. int y=((x>>i)&1),p=((k>>i)&1);
  6. if(p==0)
  7. res+=1ll*size[chd[now][y^1]];
  8. now=chd[now][y^p];
  9. }
  10. res+=size[now];
  11. return res;
  12. }

但是这样并不行,因为我们提前把建出来了,这样会两个异或起来不能成为一个区间的数的贡献加进答案。

可以边修改边查询,把直接改成经过的所有点都增加,在每一次查询前插入上一个前缀和,这样我们可以保证查询的所有贡献都是有效的了。

复杂度

代码

记得开

  1. #include<stdio.h>
  2. const int maxn=1000005,maxk=30;
  3. int n,k,tot,root;
  4. int a[maxn],sum[maxn],chd[maxn*maxk][2],size[maxn*maxk];
  5. long long ans;
  6. void insert(int x){
  7. int now=root;
  8. for(int i=30;i>=0;i--){
  9. int y=((x>>i)&1);
  10. if(chd[now][y]==0)
  11. chd[now][y]=++tot;
  12. size[now]++,now=chd[now][y];
  13. }
  14. size[now]++;
  15. }
  16. long long calc(int x,int k){
  17. int now=root;
  18. long long res=0;
  19. for(int i=30;i>=0;i--){
  20. int y=((x>>i)&1),p=((k>>i)&1);
  21. if(p==0)
  22. res+=1ll*size[chd[now][y^1]];
  23. now=chd[now][y^p];
  24. }
  25. res+=size[now];
  26. return res;
  27. }
  28. int main(){
  29. root=tot=1;
  30. scanf("%d%d",&n,&k);
  31. for(int i=1;i<=n;i++){
  32. scanf("%d",&a[i]);
  33. sum[i]=sum[i-1]^a[i];
  34. }
  35. for(int i=1;i<=n;i++){
  36. insert(sum[i-1]);
  37. ans+=calc(sum[i],k);
  38. }
  39. printf("%lld\n",ans);
  40. return 0;
  41. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注