CF 242E [线段树区间亦或+求和]
题意:给你n个数,有两种操作,op=1,对从l到r的数求和,op=2,对从l到r的值xor val。
思路:由于亦或是位运算,我们可以考虑位运算的关系,
1 xor 1=0,
0 xor 1=1,
1 xor 0=1;
0 xor 0=0;
可以看出0 xor x=x;
1 xor 1=0,1 xor 0=1,就是1变0,0变1;
我们把每个数都拆成2进制,由于是求和,只考虑每一位上1的个数,按位展开求和即可。那么我们亦或一个数也是只亦或位=1的时候,开20颗线段树就可以维护每一位的状态。
其实就是普通的区间更新区间求和问题,只不过线段树是二维的
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
int b[maxn];
struct node
{
int l,r,flag,sum;
} a[21][maxn<<2];
void pushup(int i,int id)
{
a[id][i].sum=a[id][i*2].sum+a[id][i*2+1].sum;
}
void build(int i,int l,int r,int id)
{
a[id][i].l=l,a[id][i].r=r,a[id][i].flag=0;
if(l==r)
{
if((b[l]>>id)&1)
a[id][i].sum=1;
else
a[id][i].sum=0;
return ;
}
int mid=(l+r)/2;
build(i*2,l,mid,id);
build(i*2+1,mid+1,r,id);
pushup(i,id);
}
void pushdown(int i,int id)
{
if(a[id][i].flag==1)
{
a[id][i].flag=0;
a[id][i*2].flag^=1,a[id][i*2+1].flag^=1;
a[id][i*2].sum=(a[id][i*2].r-a[id][i*2].l+1)-a[id][i*2].sum;
a[id][i*2+1].sum=(a[id][i*2+1].r-a[id][i*2+1].l+1)-a[id][i*2+1].sum;
}
}
void update(int i,int L,int R,int l,int r,int id)
{
if(l<=L&&r>=R)
{
a[id][i].sum=(R-L+1)-a[id][i].sum;
a[id][i].flag^=1;
return ;
}
pushdown(i,id);
int mid=(L+R)/2;
if(l<=mid)
update(i*2,L,mid,l,r,id);
if(r>mid)
update(i*2+1,mid+1,R,l,r,id);
pushup(i,id);
}
ll query(int i,int L,int R,int l,int r,int id)
{
if(l<=L&&r>=R)
{
return a[id][i].sum;
}
pushdown(i,id);
int mid=(L+R)/2;ll ans=0;
if(l<=mid)
ans+=query(i*2,L,mid,l,r,id);
if(r>mid)
ans+=query(i*2+1,mid+1,R,l,r,id);
return ans;
}
int main()
{
int n,m,op,x,y,val;
scanf("%d",&n);
for(int i=1; i<=n; i++)
scanf("%d",&b[i]);
for(int i=0; i<=20; i++)
build(1,1,n,i);
scanf("%d",&m);
while(m--)
{
scanf("%d",&op);
if(op==1)
{
scanf("%d%d",&x,&y);
ll ans=0;
for(int i=0; i<=20; i++)
ans+=1ll*(1ll*1<<i)*(1ll*query(1,1,n,x,y,i));
printf("%lld\n",ans);
}
else
{
scanf("%d%d%d",&x,&y,&val);
for(int i=0; i<=20; i++)
{
if((val>>i)&1)
update(1,1,n,x,y,i);
}
}
}
}