关于区间异或的线段树

Posted starve

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于区间异或的线段树相关的知识,希望对你有一定的参考价值。

题:https://codeforces.com/problemset/problem/242/E?csrf_token=e91633dfd98d038f51cc388731fe3f4d

题意:俩个操作,操作1:(l,r,x),区间[l,r]的数全部异或上x。操作2:(l ,r)输出区间[l,r]和;

分析:对数组a建线段树,对于线段树的每一个节点进行二进制拆位,每个位就统计有多少个1,更新操作对于涵盖区间的二进制位就等于其长度减去更新前的1的个数;

技术图片
#include<bits/stdc++.h>
using namespace std;
#define lson root<<1,l,midd
#define rson root<<1|1,midd+1,r
typedef long long ll;
const int N=30;
const int M=1e5+5;
ll lz[M<<2][N],tr[M<<2][N],a[M];
void up(int root){
    for(int i=0;i<=20;i++)
        tr[root][i]=tr[root<<1][i]+tr[root<<1|1][i];
}
void build(int root,int l,int r){
    if(l==r){
        int x=a[l];
        for(int i=0;i<=20;i++){
            if(x&(1<<i)){
                tr[root][i]=1;
            }
        }
        return ;
    }
    int midd=(l+r)>>1;
    build(lson);
    build(rson);
    up(root);
}
void pushdown(int root,int l,int r){
    for(int i=0;i<=20;i++){
        if(lz[root][i]){
           int midd=(l+r)>>1;
            tr[root<<1][i]=midd-l+1-tr[root<<1][i];
            tr[root<<1|1][i]=r-midd-tr[root<<1|1][i];
            lz[root<<1][i]^=lz[root][i];
            lz[root<<1|1][i]^=lz[root][i];
            lz[root][i]=0;
        }

    }
}
void update(int L,int R,int x,int root,int l,int r){
    if(L<=l&&r<=R){
        for(int i=0;i<=20;i++)
            if(x&(1<<i)){
                tr[root][i]=r-l+1-tr[root][i];///记录i位置二进制的个数
                lz[root][i]^=1;
            }
        return;

    }
    pushdown(root,l,r);
    int midd=(l+r)>>1;
    if(L<=midd)
        update(L,R,x,lson);
    if(R>midd)
        update(L,R,x,rson);
    up(root);
}
ll query(int L,int R,int root,int l,int r){
    if(L<=l&&r<=R){
        ll ans=0;
        for(int i=0;i<=20;i++){
            ans+=tr[root][i]*(1ll<<i);
        }
        return ans;
    }
    pushdown(root,l,r);
    int midd=(l+r)>>1;
    ll res=0;
    if(L<=midd)
        res=query(L,R,lson);
    if(R>midd)
        res+=query(L,R,rson);
    return res;
}
int main(){
    int n,m;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%lld",&a[i]);
    build(1,1,n);
    scanf("%d",&m);
    while(m--){
        int op;
        scanf("%d",&op);
        if(op==1){
            int l,r;
            scanf("%d%d",&l,&r);
            printf("%lld
",query(l,r,1,1,n));
        }
        else{
            int l,r,x;
            scanf("%d%d%d",&l,&r,&x);
            update(l,r,x,1,1,n);
        }
    }
    return 0;
}
View Code

 

以上是关于关于区间异或的线段树的主要内容,如果未能解决你的问题,请参考以下文章

CF703D Mishka and Interesting sum

线段树区间异或+区间和并+查询最区间大连续1的个数

字典树_异或和

[ [Ynoi2013] 无力回天 NOI2017 ] 解题报告

bzoj4025 二分图

与或异或线段树