关于区间异或的线段树
Posted starve
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于区间异或的线段树相关的知识,希望对你有一定的参考价值。
题:https://codeforces.com/problemset/problem/242/E?csrf_token=e91633dfd98d038f51cc388731fe3f4d
题意:俩个操作,操作1:(l,r,x),区间[l,r]的数全部异或上x。操作2:(l ,r)输出区间[l,r]和;
分析:对数组a建线段树,对于线段树的每一个节点进行二进制拆位,每个位就统计有多少个1,更新操作对于涵盖区间的二进制位就等于其长度减去更新前的1的个数;
#include<bits/stdc++.h> using namespace std; #define lson root<<1,l,midd #define rson root<<1|1,midd+1,r typedef long long ll; const int N=30; const int M=1e5+5; ll lz[M<<2][N],tr[M<<2][N],a[M]; void up(int root){ for(int i=0;i<=20;i++) tr[root][i]=tr[root<<1][i]+tr[root<<1|1][i]; } void build(int root,int l,int r){ if(l==r){ int x=a[l]; for(int i=0;i<=20;i++){ if(x&(1<<i)){ tr[root][i]=1; } } return ; } int midd=(l+r)>>1; build(lson); build(rson); up(root); } void pushdown(int root,int l,int r){ for(int i=0;i<=20;i++){ if(lz[root][i]){ int midd=(l+r)>>1; tr[root<<1][i]=midd-l+1-tr[root<<1][i]; tr[root<<1|1][i]=r-midd-tr[root<<1|1][i]; lz[root<<1][i]^=lz[root][i]; lz[root<<1|1][i]^=lz[root][i]; lz[root][i]=0; } } } void update(int L,int R,int x,int root,int l,int r){ if(L<=l&&r<=R){ for(int i=0;i<=20;i++) if(x&(1<<i)){ tr[root][i]=r-l+1-tr[root][i];///记录i位置二进制的个数 lz[root][i]^=1; } return; } pushdown(root,l,r); int midd=(l+r)>>1; if(L<=midd) update(L,R,x,lson); if(R>midd) update(L,R,x,rson); up(root); } ll query(int L,int R,int root,int l,int r){ if(L<=l&&r<=R){ ll ans=0; for(int i=0;i<=20;i++){ ans+=tr[root][i]*(1ll<<i); } return ans; } pushdown(root,l,r); int midd=(l+r)>>1; ll res=0; if(L<=midd) res=query(L,R,lson); if(R>midd) res+=query(L,R,rson); return res; } int main(){ int n,m; scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%lld",&a[i]); build(1,1,n); scanf("%d",&m); while(m--){ int op; scanf("%d",&op); if(op==1){ int l,r; scanf("%d%d",&l,&r); printf("%lld ",query(l,r,1,1,n)); } else{ int l,r,x; scanf("%d%d%d",&l,&r,&x); update(l,r,x,1,1,n); } } return 0; }
以上是关于关于区间异或的线段树的主要内容,如果未能解决你的问题,请参考以下文章
CF703D Mishka and Interesting sum