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 2105 Digits Count 线段树

FZU 2105Digits Count(线段树 + 成段更新)

FZU 2105 Digits Count(按位维护线段树)

FZU2105 Digits Count(按位建线段树)题解

LeetCode Count Numbers with Unique Digits

357. Count Numbers with Unique Digits