题目:进来看看呢,客官
题意:
一个定长的数组,然后他有两个操作;
①查询区间 【L,R】的区间和。
②一个更下新操作,对区间【L,R】内的数分别与一个固定的值X做异或操作,结果作为这个位置的新的值。
思路:
异或操作是在二进制的基础上进行的,所以我们开20棵线段树,每一颗线段树 去维护一个二进制位的1个数。
至于为什么存1的个数,我们来看看一个例子。
例如,一个数组有四个数字,1到4分别为5,6,7,8;
他们的 二进制分别是:
5=0101;6=0110,7=0111,8=1000;
这个数组的和是26; 5+6+7+8=26; 让我们来看一个 神奇的事情(其实很简单)
0 1 0 1
0 1 1 0
0 1 1 1
1 0 0 0
所有的二进制第一位 1 的个数之和为 1 ,第二位 1 的个数之和 3 ,第三位 1 的个数 2 ,第四位一的个数 2;
1 x 23+ 3 x 22+2 x 21+2 x 20=26;
所以 我们知道了一的个数就可以计算得到在这个区间的区间和。
那么更新操作如何做呢 ,我们为你试一下吧。
让我们用 4 对这个区间每个值求异或。
4 ^ 5 = 0100^0101 = 0001 = 1;
4 ^ 6 = 0100^0110 = 0010 = 2;
4 ^ 7 = 0100^0111 = 0011 = 3;
4 ^ 8 = 0100^1000 = 1100 = 12;
这时区间和为 18; 同样可以用上面的方法去验证。
这时我们来看看 一的个数的变化情况,
0 0 0 1
0 0 1 0
0 0 1 1
1 1 0 0
只有第二位一的个数发生了变化,因为这一位异或的是 1 而其余为为0 .
那么 它到底发生了什么变化。
之前为 3 现在为 1 而区间长度为 4 . 对 ,就是你肉眼可见的简单。
每次异或为0可以不用管,因为0 XOR 0=0, 0 XOR 1=1;
异或位 为1 时 更新 区间的 1 的个数等于区间长度减去之前区间的 1 的个数。
因为区间更新 所以要 做懒惰标记 。
细节看代码吧 。
注意跟新时候的懒惰标记的更新。
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
struct node{
int b,e,num,lazy;
}t[25][maxn<<2];
int arr[maxn];
ll ans;
void push_up(int id,int node){
t[id][node].num=t[id][node<<1].num+t[id][node<<1|1].num;
}
void create(int bb,int ee,int id,int node){
t[id][node].b=bb;
t[id][node].e=ee;
t[id][node].lazy=0;
t[id][node].num=0;
if(bb==ee){
if(arr[bb]&(1<<id)){
t[id][node].num=1;
}
return ;
}
int mid = (bb+ee)>>1;
create(bb,mid,id,node<<1);
create(mid+1,ee,id,node<<1|1);
push_up(id,node);
}
void push_down(int id,int node){
if(t[id][node].lazy==1){
int len;
t[id][node<<1].lazy=t[id][node<<1].lazy^1;
t[id][node<<1|1].lazy=t[id][node<<1|1].lazy^1;
len=t[id][node<<1].e-t[id][node<<1].b+1;
t[id][node<<1].num=len-t[id][node<<1].num;
len=t[id][node<<1|1].e-t[id][node<<1|1].b+1;
t[id][node<<1|1].num=len-t[id][node<<1|1].num;
t[id][node].lazy=0;
}
}
void update(int bb,int ee,int id,int node){
if(bb<=t[id][node].b && ee>=t[id][node].e){
t[id][node].lazy^=1;
int len=t[id][node].e-t[id][node].b+1;
t[id][node].num=len-t[id][node].num;
return ;
}
int mid=(t[id][node].b+t[id][node].e)>>1;
push_down(id,node);
if(bb>mid) update(bb,ee,id,node<<1|1);
else if(ee<=mid) update(bb,ee,id,node<<1);
else {
update(bb,mid,id,node<<1);
update(mid+1,ee,id,node<<1|1);
}
push_up(id,node);
}
int query(int bb,int ee,int id,int node){
if(bb<=t[id][node].b && ee>=t[id][node].e){
return t[id][node].num;
}
push_down(id,node);
int mid = (t[id][node].b+t[id][node].e)>>1;
if(bb>mid) return query(bb,ee,id,node<<1|1);
else if(ee<=mid) return query(bb,ee,id,node<<1);
else {
return query(bb,mid,id,node<<1)+query(mid+1,ee,id,node<<1|1);
}
}
int main()
{
int n,m;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&arr[i]);
for(int i=21;i>=0;--i){
create(1,n,i,1);
}
scanf("%d",&m);
for(int i=0;i<m;i++){
ans=0;
int opt,l,r,x;
scanf("%d%d%d",&opt,&l,&r);
if(opt==1){
for(int i=21;i>=0;--i){
ans+=(query(l,r,i,1)*(pow(2,i)));
}
printf("%I64d\n",ans);
}
if(opt==2){
scanf("%d",&x);
for(int i=21;i>=0;--i){
if(x&(1<<i)){
update(l,r,i,1);
}
}
}
}
return 0;
}