洛谷题目链接 https://www.luogu.com.cn/problem/CF242E
题目大意 :给你一个长度为n的序列a,有两种操作
1 对[l,r]区间求和;
2 对[l,r]区间的每一个数异或上x;
解析 :标准的线段树区间求和以及区间修改,首先异或的一个性质,你对一个区间异或上n个数,无论先后顺序,最后结果都一样,因此很容易想到直接用一个懒标记来存要异或的数字,不到迫不得已不下推。问题的关键在于,懒标记如何下推,以及区间如何更新,如果你单纯那数字来进行操作,显然行不通,因为a^c+b^c并不等于(a+b)^c,不满足结合性,所以你没办法直接对一个大区间修改,但又要求和。所以这里要用到二进制拆分。
二进制拆分就是把每一个数字,用二进制来表示,用一个数组 f[k][i]来表示线段树下标为K的区间,这个区间的和的二进制第i位是几,注意这里的二进制是不进位的,比如1号区间是数字4,二进制拆完后是0100,而2号区间是数字14,二进制是1110,则他们的父亲区间的二进制表示为1210,直接按位加就行,1210,转成10进制就是1*2的3次方+2*2的2次方+1*2的1次方等于18。这样每个区间的和就已经表示完了。这样表示过后就能进行区间修改操作了。
异或操作,相同为0,不同为1,对于每次要异或操作的数字x,它的每一位二进制位如果是0,0^0=0, 0^1=1, 相当于对我们没有进行修改操作,我们就不看,如果是1,1^0=1, 1^1=0, 相当宇对我们的二进制位进行了一个取反操作,那就很简单了,我们对于要异或的数字的每一位1,进行对应位置修改就行。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,mod=998244353;
int f[400005][33];//f[k][i]代表线段树下标为K的区间里,这个数字二进制第i位是多少
ll lazy[400005];//异或的懒标记
ll c[35];
ll a[100005];
void build(int l,int r,int k)
{
lazy[k]=0;
if(l==r)
{
ll t=a[l];
for(int i=0;i<33;i++)
{
if((t>>i)&1)f[k][i]=1;//把每个数字拆成二进制
}
return ;
}
int mid=(l+r)/2;
build(l,mid,k*2);
build(mid+1,r,k*2+1);
for(int i=0;i<33;i++)
{
f[k][i]=f[k*2][i]+f[k*2+1][i];//合并左儿子和右儿子的二进制,不进位加法
}
}
void pushdown(int l,int r,int k)
{
if(lazy[k])
{
int mid=(l+r)/2;
for(int i=0;i<33;i++)
{
if((lazy[k]>>i)&1)
{
f[k*2][i]=(mid-l+1)-f[k*2][i];//懒标记下推左右儿子
f[k*2+1][i]=(r-mid)-f[k*2+1][i];
}
}
lazy[k*2]^=lazy[k];
lazy[k*2+1]^=lazy[k];
lazy[k]=0;
}
}
void update(int l1,int r1,int l,int r,int k,ll x)
{
if(l>r1||r<l1)
{
return;
}
if(l1<=l&&r<=r1)
{
lazy[k]^=x;
for(int i=0;i<33;i++)//对要异或的数字二进制拆分
{
if((x>>i)&1)
{
f[k][i]=(r-l+1)-f[k][i];//当异或的这个数的二进制位为1时,要进行更新
}
}
return;
}
pushdown(l,r,k);
int mid=(l+r)/2;
update(l1,r1,l,mid,k*2,x);
update(l1,r1,mid+1,r,k*2+1,x);
for(int i=0;i<33;i++)
{
f[k][i]=f[k*2][i]+f[k*2+1][i];
}
}
ll query(int l1,int r1,int l,int r,int k)
{
if(l>r1||r<l1)
{
return 0;
}
if(l1<=l&&r<=r1)
{
ll ans=0;
for(int i=0;i<33;i++)
{
ans+=c[i]*f[k][i];//求值
}
return ans;
}
pushdown(l,r,k);
int mid=(l+r)/2;
return query(l1,r1,l,mid,k*2)+query(l1,r1,mid+1,r,k*2+1);
}
int main()
{
c[0]=1;
for(int i=1;i<=32;i++)
{
c[i]=c[i-1]*2;
}
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
build(1,n,1);
int q,op,l,r;
ll x;
cin>>q;
while(q--)
{
cin>>op;
if(op==1)
{
cin>>l>>r;
ll ans=query(l,r,1,n,1);
cout<<ans<<endl;
}
else
{
cin>>l>>r>>x;
update(l,r,1,n,1,x);
}
}
}