【线段树 && 题解】 Queries

题目传送门

题目描述:

在这里插入图片描述


S o l u t i o n Solution Solution

由于数据范围中 x x x的最大值也就 1000 1000 1000,很容易让我们想到一些位的操作。

然后题目中的单点修改、区间查询又让我们很自然的想到了线段树。

所以,我们可以开十棵线段树,一次存储每一位的异或情况。
在线段树中我们设置以下变量:
v 0 : 当 前 区 间 中 异 或 和 为 0 的 子 区 间 的 个 数 v_0:当前区间中异或和为0的子区间的个数 v00
v 1 : 当 前 区 间 中 异 或 和 为 1 的 子 区 间 的 个 数 v_1:当前区间中异或和为1的子区间的个数 v11
l 0 / 1 : 当 前 区 间 中 以 做 断 电 开 始 的 异 或 和 为 0 / 1 的 子 区 间 个 数 l_{0/1}:当前区间中以做断电开始的异或和为0/1的子区间个数 l0/10/1
r 0 / 1 : 同 l r_{0/1}:同l r0/1l
s u m : 表 示 当 前 区 间 的 异 或 和 sum:表示当前区间的异或和 sum

接下来就考虑如何转移
设当前节点为 p p p,他的两个儿子分别是 x , y x,y x,y
对于 s u m sum sum的转移显然, t r p . s u m = t r x . s u m    x o r    t r y . s u m tr_{p.sum} = tr_{x.sum} \ \ xor\ \ tr_{y.sum} trp.sum=trx.sum  xor  try.sum

t r v 0 x l 0 tr_{v0}x_{l0} trv0xl0

接下来考虑 v 0 , v 1 v_0,v_1 v0,v1的转移,我们这里就以 v 0 v_0 v0为例, v 1 v_1 v1同理:
首先就是直接从 x x x y y y中转移过来,即 p v 0 = x v 0 + y v 0 p_{v0}=x_{v0}+y_{v0} pv0=xv0+yv0
然后我们考虑左右区间合并的情况。
由于00/11异或起来为0
所以还需要加上: x r 0 ∗ y l 0 + x r 1 ∗ y l 1 x_{r0}*y_{l0}+x_{r1}*y_{l1} xr0yl0+xr1yl1
为什么是*呢?组合一下就好了
因此:
p v 0 = x v 0 + y v 0 + x r 0 ∗ y l 0 + x r 1 ∗ y l 1 p_{v0}=x_{v0}+y_{v0}+x_{r0}*y_{l0}+x_{r1}*y_{l1} pv0=xv0+yv0+xr0yl0+xr1yl1

v 1 v1 v1也是同样。

然后就是 l 和 r l和r lr的转移,这里就以 l l l的为例:
同样, p l 0 = x l 0 , p l 1 = p_{l0}=x_{l0},p_{l1}= pl0=xl0pl1= x l 1 x_{l1} xl1,直接加上左区间的。
接着就分情况讨论:
1、如果 x . s u m = 0 x.sum=0 x.sum=0,那么 l 0 l0 l0直接加上 y l 0 y_{l0} yl0 l 1 l1 l1直接加上 y l 1 y_{l1} yl1
2、如果 x . s u m = 1 x.sum=1 x.sum=1,那么 l 0 l0 l0直接加上 y l 1 y_{l1} yl1 l 1 l1 l1直接加上 y l 0 y_{l0} yl0

r的更新同样。

接下来就是一下实现以及细节的问题啦!在程序中解决吧!


C o d e Code Code

#include<bits/stdc++.h>
using namespace std;

const int N = 100010 , P = 4e3+1; 

int n,m;

struct Tr{
	int v0,v1,l0,l1,r0,r1,sum;
};

struct trr{
	Tr tr[4*N][10];
//	void build(int p,int l,int r,int k){
//		tr[p][k].l = l; tr[p][k].r = r;
//		if (l == r) return;
//		int mid = l + r >> 1;
//		build(p<<1,l,mid,k); build(p<<1|1,mid+1,r,k);
//		return;
//	}
	Tr Merge(Tr x,Tr y){
		Tr t = {};
		t.v0 = x.v0 + y.v0 + x.r1 * y.l1 % P + x.r0 * y.l0 % P;
		t.v0%=P;
		t.v1 = x.v1 + y.v1 + x.r1 * y.l0 % P + x.r0 * y.l1 % P;
		t.v1%=P;
		if (x.sum == 0) t.l0 = x.l0 + y.l0 , t.l1 = x.l1 + y.l1;
		else t.l0 = x.l0 + y.l1 , t.l1 =  x.l1 + y.l0;
		t.l0%=P; t.l1%=P;
		if (y.sum == 0) t.r0 = y.r0 + x.r0 , t.r1 = y.r1 + x.r1;
		else t.r0 = y.r0 + x.r1 , t.r1 = y.r1 + x.r0;
		t.r0%=P; t.r1%=P;
		t.sum = x.sum^y.sum; t.sum%=P;
		return t;
	}
	void change(int p,int l,int r,int x,int k,int v){
//		int l = tr[p][k].l ,r = tr[p][k].r;
		if (l == r){
			if (v == 0) tr[p][k].l0 = tr[p][k].r0 = tr[p][k].v0 = 1,tr[p][k].l1 = tr[p][k].r1 = tr[p][k].v1 = tr[p][k].sum = 0;
			else tr[p][k].l0 = tr[p][k].r0 = tr[p][k].v0 = 0,tr[p][k].l1 = tr[p][k].r1 = tr[p][k].v1 = tr[p][k].sum = 1;
			return;
		}
		int mid = l + r >> 1;
		if (x <= mid) change(p<<1,l,mid,x,k,v);
		if (x > mid) change(p<<1|1,mid+1,r,x,k,v);
		tr[p][k] = Merge(tr[p<<1][k],tr[p<<1|1][k]);
	}
	Tr ask(int p,int L,int R,int k,int l,int r){
//		int L = tr[p][k].l , R = tr[p][k].r;
//		cout<<p<<' '<<k<<' '<<endl; 
//		cout<<"l = "<<L<<' '<<R<<endl; 
		if (l <= L && R <= r) return tr[p][k];
		Tr tr1 = {} , tr2 = {};
		int mid = L + R >> 1;
//		if (l <= mid && r > mid) return Merge(ask(p<<1,L,mid,k,l,r),ask(p<<1|1,mid+1,R,k,l,r));
//		else if (l <= mid) return ask(p<<1,L,mid,k,l,r);
//		else if (r > mid) return ask(p<<1|1,mid+1,R,k,l,r);
		if (l <= mid) tr1 = ask(p<<1,L,mid,k,l,r);
		if (r > mid) tr2 = ask(p<<1|1,mid+1,R,k,l,r);
		return Merge(tr1,tr2);
	}
}tr;

void Change(int x,int X){
	for (int i = 1; i <= 10; i++)
	  tr.change(1,1,n,x,i,X&(1<<i-1));//同样的更新
}

int power(int x,int y){
	int sum = 1;
	while (y){
		if (y&1) sum = (sum * x)%P;
		x = (x*x)%P; y>>=1;
	}
	return sum%P;
}

int Ask(int x,int y){
	int ans = 0;
//	tr.ask(1,1,x,y);
	for (int i = 1; i <= 10; i++)
	  ans+=tr.ask(1,1,n,i,x,y).v1*power(2,i-1)%P , ans%=P;//计算贡献
	return ans;
}

int main(){
	scanf("%d %d",&n,&m);
	for (int i = 1; i <= n; i++){
		int x; scanf("%d",&x);
		for (int j = 1; j <= 10; j++) tr.change(1,1,n,i,j,x&(1<<(j-1)));//将第i个数的第j为改成x&(1<<(j-1))
	}
	for (int i = 1; i <= m; i++){
		int op,x,y;
		scanf("%d %d %d",&op,&x,&y);
		if (op == 1) Change(x,y);
		else printf("%d\n",Ask(x,y));
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值