题意:有n个数,m个操作。操作有两种,操作1是查询 [l, r] 的区间和,操作2是将区间 [l, r] 的每一位异或x。
思路:按二进制位建树,每个节点用于统计1的个数,添加懒惰标记,记录该节点是否异或,同时需要异或是就用该节点的区间长度减去1的个数更新
感想:线段树真神奇
#include <bits/stdc++.h>
const int maxn = 100005;
using namespace std;
typedef long long ll;
int sum[26][maxn<<2];
bool lazy[26][maxn<<2];
int ans[26];
void fen(int x, int pos){
int ji = 0;
while(x){
sum[ji++][pos] = x%2;
x/=2;
}
}
void fen1(int x, int pos, int len){
int ji = 0;
while(x){
if(x%2){
sum[ji][pos] = len - sum[ji][pos];
lazy[ji][pos] = lazy[ji][pos]^1;
}
x/=2;
ji++;
}
}
void shang(int rt){
for(int i=0; i<22; i++){
sum[i][rt] = sum[i][rt<<1] + sum[i][rt<<1|1];
}
}
void xia(int rt, int len){
for(int i=0; i<22; i++){
if(lazy[i][rt]){
sum[i][rt<<1] = (len - (len>>1)) - sum[i][rt<<1];
sum[i][rt<<1|1] = (len>>1) - sum[i][rt<<1|1];
lazy[i][rt<<1] ^= 1;
lazy[i][rt<<1|1] ^= 1;
lazy[i][rt] = 0;
}
}
}
void chuang(int l, int r, int rt){
if(l == r){
int t;
scanf("%d", &t);
fen(t, rt);//将该位上的数分解到每一颗树的相
return ;
}
int m = (l+r)>>1;
chuang(l, m, rt<<1);
chuang(m+1, r, rt<<1|1);
shang(rt);
}
void geng(int l, int r, int rt, int L, int R, int x){//更新维护
if(L <= l && R >=r){
fen1(x, rt, r-l+1);
return ;
}
int m = (l+r)>>1;
xia(rt, r-l+1);
if(L <= m)
geng(l, m, rt<<1, L, R, x);
if(R > m)
geng(m+1, r, rt<<1|1, L, R, x);
shang(rt);
}
void cha(int l, int r, int rt, int L, int R){//查询
if(L<=l && R>=r){
for(int i=0; i<22; i++)
ans[i] += sum[i][rt];
return ;
}
int m = (l+r)>>1;
xia(rt, r-l+1);
if(L <= m)
cha(l, m, rt<<1, L, R);
if(R > m)
cha(m+1, r, rt<<1|1, L, R);
}
int main()
{
int n;
scanf("%d", &n);
chuang(1, n, 1);//创建树
int m;
scanf("%d", &m);
while(m--){
int op, l, r;
scanf("%d%d%d", &op, &l, &r);
if(op == 1){
memset(ans, 0, sizeof(ans));
cha(1, n, 1, l, r);
ll he = 0;
for(int i=21; i>=0; i--){
he = he*2L + ans[i]*1L;
}
cout<<he<<endl;
}
else{
int x;
scanf("%d", &x);
geng(1, n, 1, l, r, x);
}
}
return 0;
}