description:

给一个序列a 对a进行 区间求和 或者 区间异或的操作 每次输出区间求和的值

solution:

对于区间求和并且带有更新操作 就想到树状数组或者线段树,剩下的就是区间异或的操作,对于位运算,一般都按每一位来看,异或操作只有对应位不同才对答案有贡献,于是我们建立一个二维线段树,维护每一位的情况,ai的值和n范围都是1e5,需要的空间为 1e5 * 4 * 20 * 2,空间是够的,剩下就是对线段树的区间更新和查询操作了,最后算答案时候乘上对应的2次幂就好

code:

#include <bits/stdc++.h>

using namespace std;

#define LL long long

const int N = 1e5 + 5;

LL sum[N << 2][22],lazy[N << 2][22],a[N];

void pushup(int id,int v){//回溯时对父节点更新
    sum[v][id] = sum[v << 1][id] + sum[v << 1 | 1][id];
}

void pushdown(int id,int l,int r,int v){//lazy操作对子节点更新
    if(lazy[v][id] == 0){
        return ;
    }

    lazy[v << 1][id] ^= 1;//lazy标记往子节点传递
    lazy[v << 1 | 1][id] ^= 1;

    int mid = l + r >> 1;

    sum[v << 1][id] = (mid - l + 1) - sum[v << 1][id];//异或取反操作 ^1
    sum[v << 1 | 1][id] = (r - mid) - sum[v << 1 | 1][id];
    lazy[v][id] = 0;
}

void build(int id,int l,int r,int v){//建树
    if(l == r){
        sum[v][id] = ((a[l] >> id)&1);//拆分每一位
        lazy[v][id] ^= 1;
        return ;
    }

    int mid = l + r >> 1;
    build(id,l,mid,v << 1);
    build(id,mid + 1,r,v << 1 | 1);
    pushup(id,v);
}

void update(int id,int L,int R,int l,int r,int v){
    if(L <= l && r <= R){
        sum[v][id] = (r - l + 1) - sum[v][id];//异或取反操作 ^1
        lazy[v][id] ^= 1;
        return ;
    }

    pushdown(id,l,r,v);

    int mid = l + r >> 1;

    if(L <= mid){
        update(id,L,R,l,mid,v << 1);
    }
    if(R > mid){
        update(id,L,R,mid + 1,r,v << 1 | 1);
    }

    pushup(id,v);
}

LL query(int id,int L,int R,int l,int r,int v){
    if(L <= l && r <= R){
        return  sum[v][id] ;
    }
    LL res = 0;
    pushdown(id,l,r,v);

    int mid = l + r >> 1;
    if(L <= mid){
        res += query(id,L,R,l,mid,v << 1);
    }
    if(R > mid){
        res += query(id,L,R,mid + 1,r,v << 1 | 1);
    }

    pushup(id,v);

    return res;
}

int main(){
    int n,q;

    scanf("%d%d",&n,&q);

    for(int i = 1;i <= n;i ++){
        scanf("%lld",&a[i]);
    }

    for(int i = 0;i <= 20;i ++){
        build(i,1,n,1);//对每一位建立线段树 维护每一位的和
    }

    int l,r,k;

    while(q --){
        int op;
        scanf("%d",&op);

        if(op == 1){
            scanf("%d%d",&l,&r);
            LL res = 0;
            for(int i = 0;i <= 20;i ++){
                res += 1LL*query(i,l,r,1,n,1)*(1LL << i);//查询答案对应二次幂
            }
            printf("%lld\n",res);
        }else{
            scanf("%d%d%d",&l,&r,&k);

            for(int i = 0;i <= 20;i ++){//对每一位进行更新操作
                if((k >> i) & 1){//当前位为1
                    update(i,l,r,1,n,1);
                }
            }
        }
    }
    return 0;
}