线段树与位运算
Posted liuzuolin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树与位运算相关的知识,希望对你有一定的参考价值。
线段树的区间修改里有对其区间数全部进行" ^ "或者" | "位运算,我们可以利用这两个位运算的特性来进行修改(这两个都只需记录1的个数即可)。
“ | ”:由于它只要二进制上有1则为1,所以我们只需知道它二进制有无1即可。有则改为区间长度个数的1。
“ ^ ":由于它二进制上两者不同才为1,所以我们只需知道它二进制有无1即可。有则改为区间长度-之前存在1的个数的1。
//https://ac.nowcoder.com/acm/contest/283/J #include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=2e5+5; const int maxnbit=27; const string SUM="SUM"; const string OR="OR"; int n,m,val; struct Tree{ int lazy; int digit[maxnbit]; }tree[maxn<<2]; void up(int k){ for(int i=0;i<25;i++){///子树1个数相加等于父亲的1的个数 tree[k].digit[i]=tree[k<<1].digit[i]+tree[k<<1|1].digit[i]; } } void pushdown(int k,int l,int r){///下传 if(l==r) return ; tree[k<<1].lazy|=tree[k].lazy; tree[k<<1|1].lazy|=tree[k].lazy; ///tree[k<<1].lazy^=tree[k].lazy; ///tree[k<<1|1].lazy^=tree[k].lazy; int mid=(l+r)>>1; for(int i=0;i<25;i++){ if(tree[k].lazy&(1<<i)){ tree[k<<1].digit[i]=mid-l+1; tree[k<<1|1].digit[i]=r-mid; ///tree[k<<1].digit[i]=mid-l+1-digit[i]; ///tree[k<<1|1].digit[i]=r-mid-digit[i]; } } tree[k].lazy=0; } void build(int k,int l,int r){ tree[k].lazy=0; if(l==r){ scanf("%d",&val); for(int i=0;i<25;i++){ if(val&(1<<i)){ tree[k].digit[i]=1; }else{ tree[k].digit[i]=0; } } return ; } int mid=(l+r)>>1; build(k<<1,l,mid); build(k<<1|1,mid+1,r); up(k); } void update(int k,int l,int r,int L,int R,int X){///[L,R]|X if(l>=L&&r<=R){ tree[k].lazy|=X; ///tree[k].lazy^=X; for(int i=0;i<25;i++){///更改第k个节点的第i位上1的个数 if(X&(1<<i)){ tree[k].digit[i]=r-l+1; } } return ; } if(tree[k].lazy) pushdown(k,l,r); int mid=(l+r)>>1; if(mid>=R) update(k<<1,l,mid,L,R,X); else if(mid<L) update(k<<1|1,mid+1,r,L,R,X); else{ update(k<<1,l,mid,L,R,X); update(k<<1|1,mid+1,r,L,R,X); } up(k); } ll ans; void query(int k,int l,int r,int L,int R){ if(l>=L&&r<=R){ for(int i=0;i<25;i++){///计算答案 ans+=1ll*tree[k].digit[i]*(1<<i); } return ; } if(tree[k].lazy) pushdown(k,l,r); int mid=(l+r)>>1; if(mid>=R) query(k<<1,l,mid,L,R); else if(mid<L) query(k<<1|1,mid+1,r,L,R); else{ query(k<<1,l,mid,L,R); query(k<<1|1,mid+1,r,L,R); } up(k); } int main(){ string q; int L,R,X; scanf("%d%d",&n,&m); build(1,1,n); while(m--){ cin>>q; if(q==SUM){ ans=0; scanf("%d%d",&L,&R); query(1,1,n,L,R); printf("%lld\n",ans); }else if(q==OR){ scanf("%d%d%d",&L,&R,&X); update(1,1,n,L,R,X); } } return 0; }
以上是关于线段树与位运算的主要内容,如果未能解决你的问题,请参考以下文章