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<bits/stdc++.h>
#define ll long long
#define N 100010
using namespace std;
int n,a[N],res,L,R,T,x;
int f[N<<2][25],tag[N<<2][25];
ll b[25],ans;//別忘記long long
int read(){ int s=0;char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)){s=(s<<1)+(s<<3)+c-'0';c=getchar();}
    return s;
}
void built(int k,int l,int r)
{
    if(l>r) return;
    if(l==r)
    {
        res=a[l];
        for(int i=0;i<21;i++)//拆位
            if((res>>i)&1) f[k][i]=1;
        return;
    }
    int mid=(l+r)>>1,cur=k<<1;
    built(cur,l,mid);
    built(cur|1,mid+1,r);
    for(int i=0;i<21;i++)
        f[k][i]=f[cur][i]+f[cur|1][i];
    return;
}
void push(int k,int l,int r,int p)//p表示第幾位
{
    f[k][p]=(r-l+1)-f[k][p];//區間取反
    if(l!=r)
    {
        int cur=k<<1;
        tag[cur][p]^=1;
        tag[cur|1][p]^=1;
    }
    tag[k][p]=0;
}
void Modify(int k,int l,int r,int p)//p同上
{
    if(tag[k][p]) push(k,l,r,p);
    if(r<L||R<l) return;
    if(L<=l&&r<=R)
    {
        push(k,l,r,p);
        return;
    }
    int mid=(l+r)>>1,cur=k<<1;
    Modify(cur,l,mid,p);
    Modify(cur|1,mid+1,r,p);
    f[k][p]=f[cur][p]+f[cur|1][p];
}
void Query(int k,int l,int r)//查詢,所有位的懶標記都要下放
{
    for(int i=0;i<21;i++)
        if(tag[k][i]) push(k,l,r,i);
    if(r<L||R<l) return;
    if(L<=l&&r<=R)
    {
        for(int i=0;i<21;i++) ans+=f[k][i]*b[i];
        return;
    }
    int mid=(l+r)>>1,cur=k<<1;
    Query(cur,l,mid);
    Query(cur|1,mid+1,r);
}
int main()
{
    int i,j;b[0]=1;
    for(i=1;i<=21;i++) b[i]=b[i-1]<<1;//初不初始化都可以,就是上面b[i]要變成1<<i
    n=read();
    for(i=1;i<=n;i++) a[i]=read();
    built(1,1,n);
    T=read();
    while(T--)
    {
        if(read()==1)
        {
            L=read();R=read();ans=0;
            Query(1,1,n);
            printf("%lld\n",ans);
        }
        else
        {
            L=read();R=read();x=read();
            for(i=0;i<21;i++)//判斷x的第i位是不是1,並進行修改
                if((x>>i)&1) Modify(1,1,n,i);
        }
    }
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章