Codeforces 242E. XOR on Segment (二维线段树 lazy操作 xor)

题目链接:

http://codeforces.com/problemset/problem/242/E

题意:

给出一个序列,有两种操作,一种是计算l到r的和,另一种是让l到r的数全部和x做异或运算。

思路:

from: http://blog.csdn.net/u013912596/article/details/39006317

很显然直接暴力是不可能的,又是两种操作,又想到了线段树。。但是这并不简单,异或操作该怎么处理?
异或是一种位运算,如果x的第j位是1,那么说明l到r的每个数的第j位都要反转,(0^1=1,1^1=0),如果是0,那么不变。既然是位运算,那么可不可以将每一位作为线段树单独维护呢?
好像可以呢!异或操作的话,相当于是一种区间操作,只需要将l到r的某些位进行反转操作不就行了吗?反转操作什么的,打上lazy tag不就OK啦,求和操作也可以计算出l到r的每一位上有多少个1来算出最后的和。
好,那么理一下思路,首先确定建立多少个线段树,数的范围是10^6,也就是说,我们最多只需要建立20个线段树即可,1e6最多20位嘛,每一位都是一颗线段树,有n个叶子节点,再写好线段树的bulid,update,query三个函数。每个节点上需要维护两个值,一个是该区间有多少个1(用于求和),一个是该区间是否反转(lazy tag)。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define MS(a) memset(a,0,sizeof(a))
#define MP make_pair
#define PB push_back
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3fLL;
inline ll read(){
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
//////////////////////////////////////////////////////////////////////////
const int maxn = 1e5+10;

struct node{
    int l,r,cnt,lazy;
}p[20][maxn<<2];

ll n,x,two[20];
int a[maxn][20];

int build(int rt,int l,int r,int na){
    if(l==r){
        p[na][rt] = node{l,r,a[l][na],0};
        return a[l][na];
    }
    int mid = (l+r)/2;
    int res = build(rt<<1,l,mid,na);
    res += build(rt<<1|1,mid+1,r,na);
    p[na][rt] = node{l,r,res,0};
    return res;
}

void upd(int rt,int l,int r,int na){
    int L = p[na][rt].l, R = p[na][rt].r;
    if(L==l && R==r){
        p[na][rt].lazy = 1-p[na][rt].lazy;
        p[na][rt].cnt = R-L+1-p[na][rt].cnt;
        return ;
    }
    if(p[na][rt].lazy){
        p[na][rt<<1].lazy = 1-p[na][rt<<1].lazy;
        p[na][rt<<1].cnt = p[na][rt<<1].r-p[na][rt<<1].l+1-p[na][rt<<1].cnt;
        p[na][rt<<1|1].lazy = 1-p[na][rt<<1|1].lazy;
        p[na][rt<<1|1].cnt = p[na][rt<<1|1].r-p[na][rt<<1|1].l+1-p[na][rt<<1|1].cnt;
        p[na][rt].lazy = 1-p[na][rt].lazy;    
    }
    int mid = (L+R)/2;
    if(mid >= r) upd(rt<<1,l,r,na);
    else if(mid < l) upd(rt<<1|1,l,r,na);
    else {
        upd(rt<<1,l,mid,na);
        upd(rt<<1|1,mid+1,r,na);
    }
    p[na][rt].cnt = p[na][rt<<1].cnt + p[na][rt<<1|1].cnt;
}

ll que(int rt,int l,int r,int na){
    int L = p[na][rt].l, R = p[na][rt].r;
    if(l==L && r==R) return p[na][rt].cnt;
    if(p[na][rt].lazy){
        p[na][rt<<1].lazy = 1-p[na][rt<<1].lazy;
        p[na][rt<<1].cnt = p[na][rt<<1].r-p[na][rt<<1].l+1-p[na][rt<<1].cnt;
        p[na][rt<<1|1].lazy = 1-p[na][rt<<1|1].lazy;
        p[na][rt<<1|1].cnt = p[na][rt<<1|1].r-p[na][rt<<1|1].l+1-p[na][rt<<1|1].cnt;
        p[na][rt].lazy = 1-p[na][rt].lazy;        
    }
    ll res = 0;
    int mid = (L+R)/2;
    if(mid >= r) res += que(rt<<1,l,r,na);
    else if(mid < l) res += que(rt<<1|1,l,r,na);
    else res += que(rt<<1,l,mid,na)+que(rt<<1|1,mid+1,r,na);
    return res;
}

int main(){
    two[0] = 1;
    for(int i=1; i<20; i++) two[i] = two[i-1]*2;
    cin >> n;
    for(int i=1; i<=n; i++){
        cin >> x;
        int j = 0;
        while(x){
            a[i][j++] = x&1;
            x >>= 1;
        }
    }
    for(int i=0; i<20; i++)
        build(1,1,n,i);
    int m = read();
    while(m--){
        int a,b,op = read();
        if(op == 1){
            scanf("%d%d",&a,&b);
            ll sum = 0;
            for(int i=0; i<20; i++)
                sum += que(1,a,b,i)*two[i];
            printf("%I64d
",sum);
        }else{
            int x;
            scanf("%d%d%d",&a,&b,&x);
            int i = 0;
            while(x){
                if(x&1) upd(1,a,b,i);
                i++; x>>=1;
            }
        }
    }

    return 0;
}
原文地址:https://www.cnblogs.com/yxg123123/p/7233226.html