有一个长50000的序列,ATB会让该人对于一个序列执行以下操作:
1. 区间求和,即输入l,r,输出
2. 区间异或,即输入l,r,k,对于l ≤ i ≤ r,将xi变为
操作次数最多为1e5。
思路:对所有数对应二进制的某一位创建线段树,总计创建20棵线段树,当区间所有点都异或k的时候,
需要做的操作只是将k的二进制中存在的位的那些棵线段树进行修改,将对应区间中的1变成0,0变成1,
表现在线段树节点上就是sum[p]=r-l+1-sum[p]。
下面上代码
#include<bits/stdc++.h>
#define ll long long
#define lson p<<1,l,mid
#define rson p<<1|1,mid+1,r
using namespace std;
const ll maxn = 1e5+10;
ll a[maxn],bin[25];
ll sum[25][maxn<<2];//每个2进制位建一棵线段树
ll lazy[25][maxn<<2];
void build(ll id,ll p,ll l,ll r){
if(l==r){
sum[id][p]=(a[l]&bin[id])? 1 : 0;
lazy[id][p]=0;
return;
}
ll mid=l+r>>1;
build(id,lson);
build(id,rson);
sum[id][p]=sum[id][p<<1]+sum[id][p<<1|1];
}
void pushdown(ll id,ll p,ll l,ll r){
if(lazy[id][p]==1){
lazy[id][p<<1]^=1;
lazy[id][p<<1|1]^=1;
lazy[id][p]=0;
ll mid=l+r>>1;
sum[id][p<<1]=(mid-l+1)-sum[id][p<<1];
sum[id][p<<1|1]=(r-mid)-sum[id][p<<1|1];
}
}
void change(ll id,ll p,ll l,ll r,ll x,ll y){
if(l>=x&&r<=y){
sum[id][p]=r-l+1-sum[id][p];
lazy[id][p]^=1;
return;
}
pushdown(id,p,l,r);
ll mid=l+r>>1;
if(x<=mid) change(id,lson,x,y);
if(y>mid) change(id,rson,x,y);
sum[id][p]=sum[id][p<<1]+sum[id][p<<1|1];
}
ll query(ll id,ll p,ll l,ll r,ll x,ll y){
if(l>=x&&r<=y) return sum[id][p];
pushdown(id,p,l,r);
ll mid=l+r>>1;
ll ret=0;
if(x<=mid) ret+=query(id,lson,x,y);
if(y>mid) ret+=query(id,rson,x,y);
return ret;
}
int main()
{
bin[0]=1;
for(ll i=1;i<25;i++) bin[i]=bin[i-1]<<1;
ll n,q;
scanf("%lld%lld",&n,&q);
for(ll i=1;i<=n;i++)
scanf("%lld",&a[i]);
for(ll i=0;i<=20;i++)
build(i,1,1,n);
while(q--){
ll op,l,r,k;
scanf("%lld%lld%lld",&op,&l,&r);
if(op==1){
ll ans=0;
for(ll i=0;i<=20;i++)
ans+=bin[i]*query(i,1,1,n,l,r);
printf("%lld\n",ans);
}
if(op==2){
ll k;
scanf("%lld",&k);
for(ll i=0;i<=20;i++)
if(k&bin[i]) change(i,1,1,n,l,r);
}
}
return 0;
}