1. 程式人生 > >牛客小白月賽9 C 紅球進黑洞【線段樹+模擬位運算】

牛客小白月賽9 C 紅球進黑洞【線段樹+模擬位運算】

時間限制:C/C++ 3秒,其他語言6秒
空間限制:C/C++ 262144K,其他語言524288K
64bit IO Format: %lld

題目描述 

在心理疏導室中有一種奇特的疏導工具,叫做紅球。紅球被提前分為了許多正方形小方格。
每當有人來找ATB做心理疏導時,ATB就會讓他去先玩紅球,然後通過紅球小格方的高度來判斷一個人的壓力程度的高低
具體地講,ATB會讓該人對於一個序列執行以下操作
1. 區間求和,即輸入l,r,輸出
2. 區間異或,即輸入l,r,k,對於l ≤ i ≤ r,將xi變為
可是ATB天天算計那麼多答案,已經對這份工作產生了厭煩,所以請你幫幫他,對於一組給定的資料,輸出對應的答案
ATB會將你感謝到爆

輸入描述:

第一行兩個整數n和m,表示數列長度和詢問次數
第二行有n個整數,表示這個數列的初始數值
接下來有m行,形如 1 l r 或者 2 l r k
分別表示查詢
或者對於l ≤ i ≤ r,將xi變為

輸出描述:

對於每一個查詢操作,輸出查詢的結果並換行

示例1

輸入

複製

10 10
8 5 8 9 3 9 8 3 3 6 
2 1 4 1
1 2 6 
2 9 10 8
1 1 7 
2 4 7 8
2 8 8 6
2 2 3 0
1 1 2 
2 9 10 4
1 2 3 

輸出

複製

33
50
13
13

備註:

1. 資料範圍
對於的資料,保證 n, m, k≤ 10
對於另外的資料,保證 n, m ≤ 50000, k ∈ {0, 1}

對於全部的資料,保證 1 ≤ n,m ≤ 105, 0≤ ai,k ≤ 105

2. 說明

表示

 

思路:

主要問題是處理Xor操作。可以用一個二維的線段樹維護,第二維儲存每一位中1的個數。然後區間更新。如果k的當前位為1,那麼將tree[v][i]中的0變為1,1變為0,即1的個數為(R-L+1)-tree[v][i]。複雜度為O(nlogn*40)。

程式碼:

#include<bits/stdc++.h>
using namespace std;
#define MAXN 100005
#define ll long long
int n,m;
ll tree[4*MAXN][40],laz[4*MAXN],que;
void build(int v,int L,int R)
{
    if(L==R)
    {
        ll d;
        scanf("%lld",&d);
        for(int i=0;d;i++)
        {
            tree[v][i]=d&1;
            d>>=1;
        }
    }
    else
    {
        int mid=(L+R)/2;
        build(v*2,L,mid);
        build(v*2+1,mid+1,R);
        for(int i=0; i<40; i++)
            tree[v][i]=tree[v*2][i]+tree[v*2+1][i];
    }
}

void pushdown(int L,int R,int v)
{
    laz[v*2]^=laz[v];
    laz[v*2+1]^=laz[v];
    int d=laz[v];
    int mid=(L+R)/2;
    for(int i=0; i<20 && d; i++,d>>=1)
    {
        if(d&1)
        {
            tree[v*2][i]=(mid-L+1)-tree[v*2][i];
            tree[v*2+1][i]=(R-mid)-tree[v*2+1][i];
        }
    }
    laz[v]=0;
}
void updata(int v,int L,int R,int ql,int qr,ll q)
{
    if(ql<=L && R<=qr)
    {
        ll d=q;
        for(int i=0; i<20 && d; i++,d>>=1)
        {
            if(d&1) tree[v][i]=(R-L+1)-tree[v][i];
        }
        laz[v]^=q;
        return;
    }
    if(laz[v]) pushdown(L,R,v);
    int mid=(L+R)/2;
    if(ql<=mid) updata(v*2,L,mid,ql,qr,q);
    if(qr>mid) updata(v*2+1,mid+1,R,ql,qr,q);
    for(int i=0; i<40; i++)
        tree[v][i]=tree[v*2][i]+tree[v*2+1][i];
}
void query(int v,int L,int R,int ql,int qr)
{
    if(ql<=L && R<=qr)
    {
        for(int i=0; i<40; i++)
            que+=(1LL<<i)*tree[v][i];
        return;
    }
    if(laz[v]) pushdown(L,R,v);
    int mid=(L+R)/2;
    if(ql<=mid) query(v*2,L,mid,ql,qr);
    if(qr>mid) query(v*2+1,mid+1,R,ql,qr);
}
int main()
{
    scanf("%d%d",&n,&m);
    memset(tree,0,sizeof tree);
    memset(laz,0,sizeof laz);
    build(1,1,n);

    for(int i=0; i<m; i++)
    {
        int op,l,r;
        ll d;
        scanf("%d",&op);
        if(op==2)
        {
            scanf("%d%d%lld",&l,&r,&d);
            updata(1,1,n,l,r,d);
        }
        else
        {
            scanf("%d%d",&l,&r);
            que=0;
            query(1,1,n,l,r);
            printf("%lld\n",que);
        }
    }
    return 0;
}