题目链接:
题意:
给定一个长度为 n 的序列,有 m 次操作:
操作有2种:
1. 区间求和,即输入l,r,输出 .
2. 区间异或,即输入l,r,k,对于,将 a[i] 变为
.
数据范围:
,
思路:
我们无法得到一个区间更新的公式来做到区间异或,但异或是2进制上的操作,我们可以想到对一个数二进制的每一位来进行操作。因为 ,所以二进制位数<20位,因此我们可以开20颗线段树,第 i 颗线段树来维护二进制第 i 位上 1 的个数。
由于0异或上任何数 = 任何数本身,所以如果要异或的 k 的二进制第 i 位上为 0,那么这位就不需要考虑了。
此外,1异或上0=1,1异或上1=0 。也就是说,异或 1 能把 0 变成 1,把 1 变成 0 。所以如果要异或的 k 的二进制第 i 位上为 1,区间更新的时候区间[l,r]中更新后 1 的个数就等以区间长度(r-l+1)减去更新前 1 的个数。(也就是更新后 1 的个数 = 更新前 0 的个数)那么现在区间更新的公式已经得到了。
最后查询的时候,区间[l,r]求和就等于区间[l,r]每一位上 1 的个数乘以当前二进制位所代表的数值。
Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX = 1e5+10;
typedef struct{
ll sum,lazy;
}Point;
int n,m;
ll a[MAX];
Point tree[25][MAX<<2];
void PushUp(int id,int root)
{
tree[id][root].sum=tree[id][root<<1].sum+tree[id][root<<1|1].sum;
}
void PushDown(int id,int root,int l,int r)
{
if(tree[id][root].lazy==1){
tree[id][root].lazy=0;
tree[id][root<<1].lazy^=1;
tree[id][root<<1|1].lazy^=1;
int mid = (l+r)>>1;
tree[id][root<<1].sum=(mid-l+1)-tree[id][root<<1].sum;
tree[id][root<<1|1].sum=(r-mid)-tree[id][root<<1|1].sum;
}
}
void build(int id,int l,int r,int root)
{
if(l==r){
tree[id][root].sum=((a[l]>>id)&1);
tree[id][root].lazy=0;
return;
}
int mid = (l+r)>>1;
build(id,l,mid,root<<1);
build(id,mid+1,r,root<<1|1);
PushUp(id,root);
return;
}
void update(int id,int L,int R,int l,int r,int root)
{
if(L<=l&&r<=R){
tree[id][root].sum=(r-l+1)-tree[id][root].sum;
tree[id][root].lazy^=1;
return;
}
int mid = (l+r)>>1;
PushDown(id,root,l,r);
if(L<=mid) update(id,L,R,l,mid,root<<1);
if(R>mid) update(id,L,R,mid+1,r,root<<1|1);
PushUp(id,root);
}
ll query(int id,int L,int R,int l,int r,int root)
{
if(L<=l&&r<=R){
return tree[id][root].sum;
}
int mid = (l+r)>>1;
PushDown(id,root,l,r);
ll ans=0;
if(L<=mid) ans+=query(id,L,R,l,mid,root<<1);
if(R>mid) ans+=query(id,L,R,mid+1,r,root<<1|1);
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
for(int i=0;i<=20;i++){
build(i,1,n,1);
}
while(m--)
{
ll op,l,r,k;
scanf("%lld",&op);
if(op==1){
scanf("%lld%lld",&l,&r);
ll ans=0;
for(int i=0;i<=20;i++){
ans+=1ll*query(i,l,r,1,n,1)*(1ll<<i);//注意1<<i会爆int
}
printf("%lld\n",ans);
}
else{
scanf("%lld%lld%lld",&l,&r,&k);
for(int i=0;i<=20;i++){
if((k>>i)&1){
update(i,l,r,1,n,1);
}
}
}
}
return 0;
}