Codeforces 242E XOR on Segment(线段树)

本文介绍了一种使用线段树解决特定区间操作问题的方法。面对大量数值和操作需求,通过建立多棵线段树分别跟踪二进制位上的1的数量,实现高效的区间求和与异或更新操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:

给你n<=1e5个数a[i]和m<=5*1e4个操作,操作1要求输出[l,r]区间的数的和,操作2要求对[l,r]区间的数都去异或x。

思路:

线段树。对于异或操作,我们想到二进制数,只有当x的二进制位为1时,被异或的数对应的二进制位取反。对于求和操作,我们如果知道二进制数每位1的个数,我们累加起来就能得到其和了。所以这里对于每一位都建立一颗树来保存1的个数,这里a[i]、x<=1e6(2^20),所以我们需要建20棵树,然后只需要对每棵树进行简单的区间更新、区间求和即可。

#include<cstdio>
typedef __int64 LL;

const int MAX=1e5+5;
int sum[25][MAX<<2],lazy[25][MAX<<2];
int n,m,a[MAX];

void PushUp(int id,int rt){
	sum[id][rt]=sum[id][rt<<1]+sum[id][rt<<1|1];
}

void PushDown(int id,int rt,int m){
	if(lazy[id][rt]){
		lazy[id][rt<<1]^=lazy[id][rt];
		lazy[id][rt<<1|1]^=lazy[id][rt];
		sum[id][rt<<1]=(m-(m>>1))-sum[id][rt<<1];
		sum[id][rt<<1|1]=(m>>1)-sum[id][rt<<1|1];
		lazy[id][rt]=0;
	}
}

void Build(int id,int l,int r,int rt){
	sum[id][rt]=lazy[id][rt]=0;
	if(l==r){
		sum[id][rt]=a[l]%2;
		a[l]/=2;
		//printf("l=%d a[l]=%d\n",l,a[l]);
		return;
	}
	int mid=(l+r)>>1;
	Build(id,l,mid,rt<<1);
	Build(id,mid+1,r,rt<<1|1);
	PushUp(id,rt);
}

void UpDate(int id,int L,int R,int l,int r,int rt){
	if(L<=l&&r<=R){
		lazy[id][rt]^=1;
		//printf("before: %d\n",sum[id][rt]);
		sum[id][rt]=(r-l+1)-sum[id][rt];
		//printf("after: %d\n",sum[id][rt]);
		//printf("l=%d r=%d\n",l,r);
		return;
	}
	PushDown(id,rt,r-l+1);
	int mid=(l+r)>>1;
	if(L<=mid) UpDate(id,L,R,l,mid,rt<<1);
	if(R>mid) UpDate(id,L,R,mid+1,r,rt<<1|1);
	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 res=0;
	int mid=(l+r)>>1;
	if(L<=mid) res+=Query(id,L,R,l,mid,rt<<1);
	if(R>mid) res+=Query(id,L,R,mid+1,r,rt<<1|1);
	return res;
}

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
	}
	for(int i=1;i<=20;i++){
		Build(i,1,n,1);
	}
	scanf("%d",&m);
	int q,l,r,x;
	while(m--){
		scanf("%d",&q);
		if(q==1){
			scanf("%d%d",&l,&r);
			LL ans=0,tmp=1;
			for(int i=1;i<=20;i++){
				int cnt=Query(i,l,r,1,n,1);
				//printf("i=%d cnt=%d\n",i,cnt);
				ans+=(LL)cnt*tmp;
				tmp*=2;
			}
			printf("%I64d\n",ans);
		}
		else{
			scanf("%d%d%d",&l,&r,&x);
			for(int i=1;i<=20;i++){
				if(x%2){
					//printf("i=%d\n",i);
					UpDate(i,l,r,1,n,1);
				}
				x/=2;
				if(!x) break;
			}
		}
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值