FZU-2105 Digits Count (两种标记成段更新)
Posted 真正的强者,从不埋怨黎明前的黑暗!!!
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FZU-2105 Digits Count (两种标记成段更新)相关的知识,希望对你有一定的参考价值。
题目大意:给n个0~15之间的数,有3种更新操作,1种询问操作。3种更新操作是:1、让某个闭区间的所有数字与一个0~15之间的数字进行逻辑与运算;2、让某个闭区间的所有数字与一个0~15之间的数字进行逻辑或运算;3、让某个闭区间的所有数字与一个0~15之间的数字进行异或运算。一种询问操作是询问某个闭区间的所有数字之和。
题目分析:所有的输入数字都是在0~15之间,可以二进制中的每一位建立线段树,建立四棵。3种更新操作实际上只有两种,即区间置位和区间反转。用两个懒惰标记,当进行区间置位更新的时候,要清除已经存在的反转标记,这样就能保证标记下传时两种标记下传的先后次序。
代码如下:
# include<iostream> # include<cstdio> # include<cstring> # include<algorithm> using namespace std; # define LL long long const int N=1000000; int n,m; int a[N+5]; int tr[4][N*4+5]; int lazy[4][N*4+5]; int lazy_xor[4][N*4+5]; char com[5]; void pushDown(int id,int rt,int l,int r) { int mid=l+(r-l)/2; if(lazy[id][rt]!=-1){ lazy_xor[id][rt<<1]=lazy_xor[id][rt<<1|1]=0; lazy[id][rt<<1]=lazy[id][rt<<1|1]=lazy[id][rt]; tr[id][rt<<1]=lazy[id][rt]*(mid-l+1); tr[id][rt<<1|1]=lazy[id][rt]*(r-mid); lazy[id][rt]=-1; } int &q=lazy_xor[id][rt]; if(q==1){ lazy_xor[id][rt<<1]^=1; lazy_xor[id][rt<<1|1]^=1; tr[id][rt<<1]=mid-l+1-tr[id][rt<<1]; tr[id][rt<<1|1]=r-mid-tr[id][rt<<1|1]; q=0; } } void pushUp(int id,int rt) { tr[id][rt]=tr[id][rt<<1]+tr[id][rt<<1|1]; } void build(int id,int rt,int l,int r) { lazy[id][rt]=-1; lazy_xor[id][rt]=0; if(l==r){ if((a[r]&(1<<id))>0) tr[id][rt]=1; else tr[id][rt]=0; }else{ int mid=l+(r-l)/2; build(id,rt<<1,l,mid); build(id,rt<<1|1,mid+1,r); pushUp(id,rt); } } void update(int id,int rt,int l,int r,int L,int R,int x) { if(L<=l&&r<=R){ if(x==2){ if(lazy[id][rt]!=-1) lazy[id][rt]^=1; else lazy_xor[id][rt]^=1; tr[id][rt]=(r-l+1)-tr[id][rt]; }else{ lazy[id][rt]=x; lazy_xor[id][rt]=0; tr[id][rt]=x*(r-l+1); } }else{ pushDown(id,rt,l,r); int mid=l+(r-l)/2; if(L<=mid) update(id,rt<<1,l,mid,L,R,x); if(R>mid) update(id,rt<<1|1,mid+1,r,L,R,x); pushUp(id,rt); } } LL query(int id,int rt,int l,int r,int L,int R) { if(L<=l&&r<=R) return tr[id][rt]; pushDown(id,rt,l,r); int mid=l+(r-l)/2; LL res=0; if(L<=mid) res+=query(id,rt<<1,l,mid,L,R); if(R>mid) res+=query(id,rt<<1|1,mid+1,r,L,R); return res; } int main() { //freopen("in.txt","r",stdin); int T; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); for(int i=0;i<n;++i) scanf("%d",a+i); for(int i=0;i<4;++i) build(i,1,0,n-1); int o,b,c; while(m--) { scanf("%s",com); if(com[0]==‘S‘){ scanf("%d%d",&b,&c); int ans=0; for(int i=0;i<4;++i){ ans+=query(i,1,0,n-1,b,c)*(1<<i); } printf("%d\n",ans); }else{ scanf("%d%d%d",&o,&b,&c); if(com[0]==‘X‘){ for(int i=0;i<4;++i) if(o&(1<<i)) update(i,1,0,n-1,b,c,2); }else if(com[0]==‘O‘){ for(int i=0;i<4;++i) if(o&(1<<i)) update(i,1,0,n-1,b,c,1); }else if(com[0]==‘A‘){ for(int i=0;i<4;++i) if(!(o&(1<<i))) update(i,1,0,n-1,b,c,0); } } } } return 0; }
以上是关于FZU-2105 Digits Count (两种标记成段更新)的主要内容,如果未能解决你的问题,请参考以下文章
FZU 2105Digits Count(线段树 + 成段更新)
FZU 2105 Digits Count(按位维护线段树)
FZU2105 Digits Count(按位建线段树)题解