首页 > 代码库 > Codeforces 242E. XOR on Segment

Codeforces 242E. XOR on Segment

题目大意:

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

做法:

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

代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#define N 100010
#define L(x) (x)<<1
#define R(x) (x)<<1|1
using namespace std;
struct node
{
    int l,r,cnt,lazy;
    node(long long ll,long long rr,long long c,long long la)
    {
        l=ll,r=rr,cnt=c,lazy=la;
    }
    node()
    {
        l=r=cnt=lazy=0;
    }
}p[20][N*4];
long long a[N][20],num[N],two[22];
long long bulid(long long id,long long l,long long r,long long na)
{
    if(l==r){p[na][id]=node(l,r,a[l][na],0);return a[l][na];}
    long long mid=(l+r)/2;
    long long cur=bulid(L(id),l,mid,na)+bulid(R(id),mid+1,r,na);
    p[na][id]=node(l,r,cur,0);
    return cur;
}
void revers(long long id,long long l,long long r,long long na)
{
    if(p[na][id].l==l && p[na][id].r==r)
    {
        p[na][id].lazy=1-p[na][id].lazy;
        p[na][id].cnt=p[na][id].r-p[na][id].l+1-p[na][id].cnt;
        return ;
    }
    if(p[na][id].lazy)//lazy tag 下放
    {
        p[na][id].lazy=0;
        p[na][L(id)].lazy=1-p[na][L(id)].lazy;
        p[na][R(id)].lazy=1-p[na][R(id)].lazy;
        p[na][L(id)].cnt=p[na][L(id)].r-p[na][L(id)].l+1-p[na][L(id)].cnt;
        p[na][R(id)].cnt=p[na][R(id)].r-p[na][R(id)].l+1-p[na][R(id)].cnt;
    }
    long long mid=(p[na][id].l+p[na][id].r)/2;
    if(mid<l) revers(R(id),l,r,na);
    else if(mid>=r) revers(L(id),l,r,na);
    else revers(L(id),l,mid,na),revers(R(id),mid+1,r,na);
    p[na][id].cnt=p[na][L(id)].cnt+p[na][R(id)].cnt;
}
long long query(long long id,long long l,long long r,long long na)
{
    if(p[na][id].l==l && p[na][id].r==r) return p[na][id].cnt;
    if(p[na][id].lazy)
    {
        p[na][id].lazy=0;
        p[na][L(id)].lazy=1-p[na][L(id)].lazy;
        p[na][R(id)].lazy=1-p[na][R(id)].lazy;
        p[na][L(id)].cnt=p[na][L(id)].r-p[na][L(id)].l+1-p[na][L(id)].cnt;
        p[na][R(id)].cnt=p[na][R(id)].r-p[na][R(id)].l+1-p[na][R(id)].cnt;
    }
    long long mid=(p[na][id].l+p[na][id].r)/2;
    if(mid>=r) return query(L(id),l,r,na);
    else if(mid<l) return query(R(id),l,r,na);
    else return query(L(id),l,mid,na)+query(R(id),mid+1,r,na);
}
int main()
{
    two[0]=1;
    for(long long i=1;i<21;i++) two[i]=two[i-1]*2;
    long long n;
    scanf("%I64d",&n);
    for(long long i=1;i<=n;i++)
    {
        scanf("%I64d",&num[i]);
        long long j=0;
        while(num[i]>0)
        {
            a[i][j++]=num[i]&1;
            num[i]>>=1;
        }
    }
    for(long long i=0;i<20;i++)
        bulid(1,1,n,i);
    long long m;
    scanf("%I64d",&m);
    while(m--)
    {
        long long ty;
        scanf("%I64d",&ty);
        if(ty==1)
        {
            long long a,b;
            scanf("%I64d%I64d",&a,&b);
            long long cur=0;
            for(long long i=0;i<20;i++)
                cur+=query(1,a,b,i)*two[i];
            cout<<cur<<endl;
        }
        else
        {
            long long a,b,x;
            scanf("%I64d%I64d%I64d",&a,&b,&x);
            for(long long i=0;i<20 && x;i++,x>>=1)
            {
                if(x&1) revers(1,a,b,i);
            }
        }
    }
    return 0;
}


Codeforces 242E. XOR on Segment