Codeforces 242E XOR on Segment(线段树)

来源:互联网 发布:华硕笔记本推荐 知乎 编辑:程序博客网 时间:2024/06/05 17:04

题意:

给你n<=1e5个数a[i]和m<=5*1e4个操作,操作1要求输出[l,r]区间的数的和,操作2要求对[l,r]区间的数都去异或x。

思路:

线段树。对于异或操作,我们想到二进制数,只有当x的二进制位为1时,被异或的数对应的二进制位取反。对于求和操作,我们如果知道二进制数每位1的个数,我们累加起来就能得到其和了。所以这里对于每一位都建立一颗树来保存1的个数,这里a[i]、x<=1e6(2^20),所以我们需要建20棵树,然后只需要对每棵树进行简单的区间更新、区间求和即可。

#include<cstdio>typedef __int64 LL;const int MAX=1e5+5;int sum[25][MAX<<2],lazy[25][MAX<<2];int n,m,a[MAX];void PushUp(int id,int rt){sum[id][rt]=sum[id][rt<<1]+sum[id][rt<<1|1];}void PushDown(int id,int rt,int m){if(lazy[id][rt]){lazy[id][rt<<1]^=lazy[id][rt];lazy[id][rt<<1|1]^=lazy[id][rt];sum[id][rt<<1]=(m-(m>>1))-sum[id][rt<<1];sum[id][rt<<1|1]=(m>>1)-sum[id][rt<<1|1];lazy[id][rt]=0;}}void Build(int id,int l,int r,int rt){sum[id][rt]=lazy[id][rt]=0;if(l==r){sum[id][rt]=a[l]%2;a[l]/=2;//printf("l=%d a[l]=%d\n",l,a[l]);return;}int mid=(l+r)>>1;Build(id,l,mid,rt<<1);Build(id,mid+1,r,rt<<1|1);PushUp(id,rt);}void UpDate(int id,int L,int R,int l,int r,int rt){if(L<=l&&r<=R){lazy[id][rt]^=1;//printf("before: %d\n",sum[id][rt]);sum[id][rt]=(r-l+1)-sum[id][rt];//printf("after: %d\n",sum[id][rt]);//printf("l=%d r=%d\n",l,r);return;}PushDown(id,rt,r-l+1);int mid=(l+r)>>1;if(L<=mid) UpDate(id,L,R,l,mid,rt<<1);if(R>mid) UpDate(id,L,R,mid+1,r,rt<<1|1);PushUp(id,rt);}int Query(int id,int L,int R,int l,int r,int rt){if(L<=l&&r<=R){return sum[id][rt];}PushDown(id,rt,r-l+1);int res=0;int mid=(l+r)>>1;if(L<=mid) res+=Query(id,L,R,l,mid,rt<<1);if(R>mid) res+=Query(id,L,R,mid+1,r,rt<<1|1);return res;}int main(){scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d",&a[i]);}for(int i=1;i<=20;i++){Build(i,1,n,1);}scanf("%d",&m);int q,l,r,x;while(m--){scanf("%d",&q);if(q==1){scanf("%d%d",&l,&r);LL ans=0,tmp=1;for(int i=1;i<=20;i++){int cnt=Query(i,l,r,1,n,1);//printf("i=%d cnt=%d\n",i,cnt);ans+=(LL)cnt*tmp;tmp*=2;}printf("%I64d\n",ans);}else{scanf("%d%d%d",&l,&r,&x);for(int i=1;i<=20;i++){if(x%2){//printf("i=%d\n",i);UpDate(i,l,r,1,n,1);}x/=2;if(!x) break;}}}return 0;}


0 0
原创粉丝点击