[线段树]区间and or xor

描述 请你维护一个线段树

支持一下操作

A x l r 区间 and x

O x l r区间 Or x

X x l r 区间 Xor x

S l r 区间求和

输入 一个数 T表示数据组数 一个数n表示初始序列长 m表示查询 随后n个整数 接下来m次询问 如上

输出 S次询问的答案

样例输入
1
4 1
1 2 4 7
S 0 2
样例输出
7
提示
为防止min-max剪枝 n=1e6 m=1e5 Ai<15 T<=3

分析:
因为Ai<15 所以可以把数拆成4个二进制位来做,然后就是简单的区间覆盖和区间翻转了
注意区间翻转时要一并把覆盖标记翻转,本人在此WA无数

代码:

#include<bits/stdc++.h>
#define N 1000005
#define lc (p<<1)
#define rc (p<<1|1)
#define mid (tr[p][wei].l+tr[p][wei].r>>1)
using namespace std;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans; 
}
int tr_tr,n,m,a[N];
struct Node{int l,r,sum,tag;}tr[N<<2][4];
inline void pushup(int p,int wei){tr[p][wei].sum=tr[lc][wei].sum+tr[rc][wei].sum;}
inline void pushnow(int p,int wei,int k){
	if(k<2)tr[p][wei].tag=k,tr[p][wei].sum=(tr[p][wei].r-tr[p][wei].l+1)*k;
	else{
		if(tr[p][wei].tag<2)tr[p][wei].tag^=1,tr[p][wei].sum=(tr[p][wei].r-tr[p][wei].l+1)*tr[p][wei].tag;
		else if(tr[p][wei].tag==2)tr[p][wei].tag=3,tr[p][wei].sum=tr[p][wei].r-tr[p][wei].l+1-tr[p][wei].sum;
		else tr[p][wei].tag=2,tr[p][wei].sum=tr[p][wei].r-tr[p][wei].l+1-tr[p][wei].sum;
	}
}
inline void pushdown(int p,int wei){if(tr[p][wei].tag!=3)pushnow(lc,wei,tr[p][wei].tag),pushnow(rc,wei,tr[p][wei].tag),tr[p][wei].tag=3;}
inline void build(int p,int l,int r,int wei){
	tr[p][wei].l=l,tr[p][wei].r=r,tr[p][wei].tag=3;
	if(l==r){tr[p][wei].sum=(a[l]&(1<<wei))>>wei;return;}
	build(lc,l,mid,wei),build(rc,mid+1,r,wei),pushup(p,wei);
}
inline void update(int p,int ql,int qr,int wei,int k){
	if(ql>tr[p][wei].r||qr<tr[p][wei].l)return;
	if(ql<=tr[p][wei].l&&tr[p][wei].r<=qr)return pushnow(p,wei,k);
	pushdown(p,wei);
	if(qr<=mid)update(lc,ql,qr,wei,k);
	else if(ql>mid)update(rc,ql,qr,wei,k);
	else update(lc,ql,mid,wei,k),update(rc,mid+1,qr,wei,k);
	pushup(p,wei); 
}
inline int query(int p,int ql,int qr,int wei){
	if(ql>tr[p][wei].r||qr<tr[p][wei].l)return 0;
	if(ql<=tr[p][wei].l&&tr[p][wei].r<=qr)return tr[p][wei].sum;
	pushdown(p,wei);
	if(qr<=mid)return query(lc,ql,qr,wei);
	if(ql>mid)return query(rc,ql,qr,wei);
	return query(lc,ql,mid,wei)+query(rc,mid+1,qr,wei); 
}
int main(){
	tr_tr=read();
	while(tr_tr--){
		n=read(),m=read();
		for(int i=1;i<=n;++i)a[i]=read();
		for(int i=0;i<4;++i)build(1,1,n,i);
		while(m--){
			char s[4];
			scanf("%s",s);
			if(s[0]=='S'){
				int l=read()+1,r=read()+1;
				int ans=0;
				for(int i=0;i<4;++i)ans+=query(1,l,r,i)<<i;
				printf("%d\n",ans);
			}
			if(s[0]=='A'){
				int x=read(),l=read()+1,r=read()+1;
				for(int i=0;i<4;++i){
					if((x&(1<<i)))continue;
					update(1,l,r,i,0);
				}
			}
			if(s[0]=='O'){
				int x=read(),l=read()+1,r=read()+1;
				for(int i=0;i<4;++i)if((x&(1<<i)))update(1,l,r,i,1);
			}
			if(s[0]=='X'){
				int x=read(),l=read()+1,r=read()+1;
				for(int i=0;i<4;++i)if((x&(1<<i)))update(1,l,r,i,2);
			}
		}
	}
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值