
思路:
因为没有关于异或的公式能对区间进行操作的
我们可以对于二进制的每一位都建一个线段树
对于每一位的区间我们是可以进行操作的
因为异或一个区间(区间中所有单点(叶子节点)的值是0或1)
这个区间的区间和就等于这个区间的长度减去原本的区间和
这样就能利用lazy对于每一位进行操作了
设 i 为左移了多少位
区间求和的时候就把答案加上每一位对应线段树的对应区间和乘上(1<<i)即可
AC代码:
#include <iostream>
#define mid (l+r>>1)
using namespace std;
typedef long long ll;
const int N=1e5+10;
struct node{
ll sum,lazy;
}tree[21][N<<2];
ll a[N];
void build(int id,int l,int r,int root){
if(l==r){
tree[id][root].sum=((a[l]>>id)&1);
return;
}
build(id,l,mid,root<<1);
build(id,mid+1,r,root<<1|1);
tree[id][root].sum=tree[id][root<<1].sum+tree[id][root<<1|1].sum;
return;
}
void pushdown(int id,int l,int r,int root){
if(tree[id][root].lazy==1){
tree[id][root].lazy=0;
tree[id][root<<1].lazy^=1;
tree[id][root<<1|1].lazy^=1;
tree[id][root<<1].sum=(mid-l+1)-tree[id][root<<1].sum;//异或的性质,区间里的1全部变成0,0全部变成了1
tree[id][root<<1|1].sum=(r-mid)-tree[id][root<<1|1].sum;
}
return;
}
ll query(int id,int l,int r,int L,int R,int root){
if(l>=L&&r<=R){
return tree[id][root].sum;
}
pushdown(id,l,r,root);
ll ans=0;
if(L<=mid)ans+=query(id,l,mid,L,R,root<<1);
if(R>mid)ans+=query(id,mid+1,r,L,R,root<<1|1);
return ans;
}
void modify(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;
}
pushdown(id,l,r,root);
if(L<=mid)modify(id,l,mid,L,R,root<<1);
if(R>mid)modify(id,mid+1,r,L,R,root<<1|1);
tree[id][root].sum=tree[id][root<<1].sum+tree[id][root<<1|1].sum;
return;
}
int main()
{
ios::sync_with_stdio(false);
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=0;i<=20;i++){//因为数据1e5,开到20够了
build(i,1,n,1);
}
while(m--){
int op;
cin>>op;
if(op==1){
ll ans=0;
ll l,r;
cin>>l>>r;
for(int i=0;i<=20;i++){
ans+=1ll*query(i,1,n,l,r,1)*(1ll<<i);//注意long long
}
cout<<ans<<endl;
}else{
ll l,r,k;
cin>>l>>r>>k;
for(int i=0;i<=20;i++){
if((k>>i)&1)modify(i,1,n,l,r,1);//因为一个数异或1的时候才会变,只看1就好了
}
}
}
return 0;
}

京公网安备 11010502036488号