CF242E XOR on Segment
分析
如果有一定树剖基础的同学,非常建议赶忙练手写一道洛谷树(这是我的题解,里边也附有洛谷树的原题链接)。
这里有一个经验,如果我们需要进行对区间进行位运算,那我们通常会开多个线段树,因为位运算可以分别对每一位来看
但这里有一些需要注意的,如果我们维护的其他值,会发生跳树操作,则这样就不行了,往往要用其他数据结构,或推公式
跳树操作是指,例如我们同时维护区间加与区间异或,则我们的区间加操作的时候,会同时波及到不同位的许多线段树,这就不行了
好啦,有了这个思路,写这个题目,简直轻松哇。
我们来看看两个操作。
区间询问
询问区间内的所有值的和,那我们只需看看对每一位而言,其上1的数量,最后累加起来就好。这里说的有些草率,到时候看代码就明白啦。
区间异或
依然考虑对每一个位,若x的当前位上是1,则代表[l,r]区间的所有数当前位都应当取反(这点很好想,推一下!!)
我们来直接看代码。
Ac_code
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
struct Node
{
int l,r,cnt,tag;//cnt维护当前位区间内1的个数,tag区间反转标记
}tr[N<<2][21];
int w[N];
int n,m;
void pushup(int u,int k)
{
tr[u][k].cnt = tr[u<<1][k].cnt + tr[u<<1|1][k].cnt;
}
void build(int u,int l,int r,int k)
{
tr[u][k] = {l,r};
if(l==r)
{
if(w[l]>>k&1) tr[u][k].cnt = 1;
return ;
}
int mid = l + r >> 1;
build(u<<1,l,mid,k),build(u<<1|1,mid+1,r,k);
pushup(u,k);
}
void pushdown(int u,int k)
{
auto &root = tr[u][k],&left = tr[u<<1][k],&right = tr[u<<1|1][k];
if(root.tag)
{
left.cnt = left.r - left.l + 1 - left.cnt;
left.tag ^= 1;
right.cnt = right.r - right.l + 1 - right.cnt;
right.tag ^= 1;
root.tag = 0;
}
}
void modify(int u,int l,int r,int k)
{
if(l<=tr[u][k].l&&tr[u][k].r<=r)
{
tr[u][k].cnt = tr[u][k].r - tr[u][k].l + 1 - tr[u][k].cnt;
tr[u][k].tag ^= 1;
return ;
}
pushdown(u,k);
int mid = tr[u][k].l + tr[u][k].r >> 1;
if(l<=mid) modify(u<<1,l,r,k);
if(r>mid) modify(u<<1|1,l,r,k);
pushup(u,k);
}
int query(int u,int l,int r,int k)
{
if(l<=tr[u][k].l&&tr[u][k].r<=r) return tr[u][k].cnt;
pushdown(u,k);
int mid = tr[u][k].l + tr[u][k].r >> 1;
int res = 0;
if(l<=mid) res += query(u<<1,l,r,k);
if(r>mid) res += query(u<<1|1,l,r,k);
return res;
}
int ksm(int a,int b)//计算位权
{
int res = 1;
while(b)
{
if(b&1) res *= a;
a*=a;
b>>=1;
}
return res;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",w+i);
for(int i=0;i<=20;i++)//对每一个线段树初始化
build(1,1,n,i);
scanf("%d",&m);
while(m--)
{
int op,l,r,x;
scanf("%d%d%d",&op,&l,&r);
if(op==1)
{
LL res = 0;
for(int i=0;i<=20;i++)
{
int t = ksm(2,i);//对每一位计算位权
res += 1ll*query(1,l,r,i)*t;//然后该位1的个数*位权
}
printf("%lld\n",res);
}
else
{
scanf("%d",&x);
for(int i=0;i<=20;i++)//若x当前位为1,则区间内所有数,该位取反
if(x>>i&1)
modify(1,l,r,i);
}
}
return 0;
}

浙公网安备 33010602011771号