题目传送门
题目描述:
S o l u t i o n Solution Solution
由于数据范围中 x x x的最大值也就 1000 1000 1000,很容易让我们想到一些位的操作。
然后题目中的单点修改、区间查询又让我们很自然的想到了线段树。
所以,我们可以开十棵线段树,一次存储每一位的异或情况。
在线段树中我们设置以下变量:
v
0
:
当
前
区
间
中
异
或
和
为
0
的
子
区
间
的
个
数
v_0:当前区间中异或和为0的子区间的个数
v0:当前区间中异或和为0的子区间的个数
v
1
:
当
前
区
间
中
异
或
和
为
1
的
子
区
间
的
个
数
v_1:当前区间中异或和为1的子区间的个数
v1:当前区间中异或和为1的子区间的个数
l
0
/
1
:
当
前
区
间
中
以
做
断
电
开
始
的
异
或
和
为
0
/
1
的
子
区
间
个
数
l_{0/1}:当前区间中以做断电开始的异或和为0/1的子区间个数
l0/1:当前区间中以做断电开始的异或和为0/1的子区间个数
r
0
/
1
:
同
l
r_{0/1}:同l
r0/1:同l
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}
xr0∗yl0+xr1∗yl1
为什么是*呢?组合一下就好了
因此:
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+xr0∗yl0+xr1∗yl1
v 1 v1 v1也是同样。
然后就是
l
和
r
l和r
l和r的转移,这里就以
l
l
l的为例:
同样,
p
l
0
=
x
l
0
,
p
l
1
=
p_{l0}=x_{l0},p_{l1}=
pl0=xl0,pl1=
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;
}