FZU 2105 线段树

题意:明显的线段树,对一个区间进行操作,操作包括与,或,异或,求和四种,也就是三种操作(均为位运算),一个询问。不过每个数字都很小,[0,16],这很奇怪,而且位运算每位之间是独立的,所以可以用四个线段树分别维护每一位,求和的时候求四次,加起来就可以。


对于与操作:如果某一位为1,操作之后不产生变化,所以只考虑0的情况,0的时候全变为0,相当于覆盖

对于或操作:如果某一位为0,不产生变化,只考虑1,相当于覆盖为1

对于异或:0,不产生变化,1相当于每一位均取反


所以整理之后有两种操作,一种是区间覆盖,一种是区间取反,这两种操作明显不是一类的,所以需要用两个延迟标记,不过两个延迟标记又产生了顺序的问题,参考题解后发现是这样处理的:当对一个区间进行操作的时候,需要先看看有没有其他的操作(两种都需要考虑),然后采取不同的操作。也就是一个区间的延迟标记不能同时有标记(如果与顺序无关的话就不用这么麻烦了),这样就木有问题了=。=


代码:

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn = 1000000+5;
bool XOR[4][maxn<<2];
int OR[4][maxn<<2];
int sum[4][maxn<<2];
int nn[maxn];
void pushup(int id,int rt){
	sum[id][rt] = (sum[id][rt<<1] + sum[id][rt<<1|1]);
}
void build(int id,int l,int r,int rt){
	XOR[id][rt] = 0;
	OR[id][rt] = -1;
	if(l == r){
		bool ii = (nn[l]&(1<<id))!=0?1:0;
		sum[id][rt] = ii;
		//cout<<id<<' '<<ii<<endl;
		return ;
	}
	int m = (l + r) >> 1;
	build(id,lson);
	build(id,rson);
	pushup(id,rt);
}
void pushdown(int id,int rt,int m){
	if(OR[id][rt] != -1){
		OR[id][rt<<1] = OR[id][rt<<1|1] = OR[id][rt];
		XOR[id][rt<<1] = XOR[id][rt<<1|1] = 0;
		sum[id][rt<<1] = OR[id][rt<<1] * (m - (m>>1));
		sum[id][rt<<1|1] = OR[id][rt<<1|1] * (m>>1);
		OR[id][rt] = -1;
	}
	if(XOR[id][rt] == 1){
		if(OR[id][rt<<1] != -1)OR[id][rt<<1] ^= 1;
		else XOR[id][rt<<1] ^= 1;
		if(OR[id][rt<<1|1] != -1)OR[id][rt<<1|1] ^= 1;
		else XOR[id][rt<<1|1] ^= 1;
		sum[id][rt<<1] = (m - (m>>1)) - sum[id][rt<<1];
		sum[id][rt<<1|1] = (m>>1) - sum[id][rt<<1|1];
		XOR[id][rt] = 0;
	}
}
void update_OR(int id,int L,int R,int c,int l,int r,int rt){
	if(L <= l&&r <= R){
		XOR[id][rt] = 0;
		OR[id][rt] = c;
		sum[id][rt] = c*(r - l  + 1);
		return ;
	}
	pushdown(id,rt,r-l+1);
	int m = (l + r) >>1;
	if(L <= m)update_OR(id,L,R,c,lson);
	if(m < R)update_OR(id,L,R,c,rson);
	pushup(id,rt);
}
void update_XOR(int id,int L,int R,int l,int r,int rt){
	if(L <= l&&r <= R){
		if(OR[id][rt] != -1){
			OR[id][rt] ^= 1;
		}
		else {
			XOR[id][rt] ^= 1;
		}
		sum[id][rt] = (r - l + 1) - sum[id][rt];
		return ;
	}
	pushdown(id,rt,r-l+1);
	int m = (l + r)>>1;
	if(L <= m)update_XOR(id,L,R,lson);
	if(m < R)update_XOR(id,L,R,rson);
	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 m = (l + r) >> 1;
	int ret = 0;
	if (L <= m) ret += query(id,L , R , lson);
	if (m < R) ret += query(id,L , R , rson);
	return ret;
}
int main(){
	//freopen("in.txt","r",stdin);
	int T;
	scanf("%d",&T);
	while(T --){
		int n,m;
		scanf("%d%d",&n,&m);
		for(int i = 1;i <= n;i ++)scanf("%d",&nn[i]);
		for(int i = 0;i < 4;i ++){
			build(i,1,n,1);//cout<<i<<"aaaaaaaaaa";
		}
		char s[20];
		int l,r,op;
		while(m --){
			scanf("%s",s);
			if(s[0] == 'S'){
				scanf("%d%d",&l,&r);l++;r++;
				int ans = 0;
				for(int i = 3;i >= 0;i --){
					ans += (1<<i)*query(i,l,r,1,n,1);
				}
				printf("%d\n",ans);
			}
			else if(s[0] == 'X'){
				scanf("%d%d%d",&op,&l,&r);l++;r++;
				for(int i = 0;i < 4;i ++){
					if(op&(1<<i))
						update_XOR(i,l,r,1,n,1);
				}
			}
			else if(s[0] == 'A'){
				scanf("%d%d%d",&op,&l,&r);l++;r++;
				for(int i = 0;i < 4;i ++){
					if(!(op&(1<<i)))
						update_OR(i,l,r,0,1,n,1);
				}
			}
			else if(s[0] == 'O'){
				scanf("%d%d%d",&op,&l,&r);l++;r++;
				for(int i = 0;i < 4;i ++){
					if(op&(1<<i))
						update_OR(i,l,r,1,1,n,1);
				}
			}
		}
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值