题意:给你一个长度为n的序列,然后给出每个ai的值。有两种操作:
1 l r代表求出区间[l,r]内的ai的和。
2 l r x代表把区间[l,r]内的所有的 ai 变成 ai ^ x。
对于每个1操作输出查询的结果。
思路:看操作很明显就是线段树的操作。但是这里注意对于异或这个运算是不满足交换律的,也就是说 a ^ (b+c) != (a ^ c + b ^ c),那么我们就没办法直接对区间进行操作了。但是,看到异或就应该想到二进制有没有方法,很明显区间ai的和也等于把每一个ai二进制拆分后统计对应二进制位上的1的个数乘以位权求和。
于是,**我们考虑对于每一个二进制位去建一棵线段树来维护这个二进制位在不同区间内1的个数,也就可以求出来区间的和了。**至于修改操作,只有当x在二进制下第i位为1的时候,异或之后才会对这个位上的对应区间产生影响。可以产生影响时,就相当于在这个区间内做0和1的翻转操作,维护一下即可。
复杂度大约:O(m * 20 * logn),2e7左右吧。
比赛时没来得及敲
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 1e5 + 7;
int n,m;ll a[MAXN],cnt[22][MAXN];
//每一个二进制位上 用一棵线段树维护区间内的1的个数
struct Tree{//每个位维护一个 sum和lazy
ll sum[MAXN<<2],lazy[MAXN<<2];
}tree[22];
void pushup(int id,int rt){
tree[id].sum[rt] = tree[id].sum[rt<<1] + tree[id].sum[rt<<1|1];
}
void pushdown(int id,int rt,int tot){
if(tree[id].lazy[rt]){
tree[id].lazy[rt<<1] ^= tree[id].lazy[rt];
tree[id].lazy[rt<<1|1] ^= tree[id].lazy[rt];
tree[id].sum[rt<<1] = (tot-tot/2) - tree[id].sum[rt<<1];
tree[id].sum[rt<<1|1] = (tot/2) - tree[id].sum[rt<<1|1];
tree[id].lazy[rt] = 0;
}
}
void build(int id,int rt,int l,int r){
tree[id].lazy[rt] = 0;
if(l == r){
tree[id].sum[rt] = cnt[id][l];
return ;
}
int mid = (l+r)>>1;
build(id,rt<<1,l,mid);build(id,rt<<1|1,mid+1,r);
pushup(id,rt);
}
void modify(int id,int rt,int l,int r,int L,int R){
if(l >= L && r <= R){
tree[id].lazy[rt] ^= 1;//异或之后 为1则代表需要翻转一下 为0的话就是翻转了偶数次 相当于这个区间内的01没有反转。
tree[id].sum[rt] = r - l + 1 - tree[id].sum[rt];
return ;
}
pushdown(id,rt,r-l+1);
int mid = (l+r)>>1;
if(L <= mid) modify(id,rt<<1,l,mid,L,R);
if(R > mid) modify(id,rt<<1|1,mid+1,r,L,R);
pushup(id,rt);
}
int query(int id,int rt,int l,int r,int L,int R){
if(l >= L && r <= R) return tree[id].sum[rt];
pushdown(id,rt,r-l+1);
int mid = (l+r)>>1;int ans = 0;
if(L <= mid) ans += query(id,rt<<1,l,mid,L,R);
if(R > mid) ans += query(id,rt<<1|1,mid+1,r,L,R);
return ans;
}
int main(){
scanf("%d",&n);
for(int i = 1;i <= n;i ++){
scanf("%d",&a[i]);
}
for(int i = 1;i <= n;i ++){
for(int j = 0;j <= 21;j ++){
cnt[j][i] = (a[i]>>j) & 1;
}
}
for(int i = 0;i <= 21;i ++) build(i,1,1,n);
scanf("%d",&m);
int op,l,r;ll x;
while(m--){
scanf("%d",&op);
if(op == 1){
scanf("%d%d",&l,&r);
ll ans = 0;
for(int i = 0;i <= 21;i ++){
ans += query(i,1,1,n,l,r) * (1ll<<i);
}
printf("%lld\n",ans);
}
else if(op == 2){
scanf("%d%d%lld",&l,&r,&x);
for(int i = 0;i <= 21;i ++){
if((x>>i)&1){
modify(i,1,1,n,l,r);
}
}
}
}
return 0;
}