1. 程式人生 > >紅球進黑洞(線段樹區間異或+區間求和)

紅球進黑洞(線段樹區間異或+區間求和)

題目連結:

紅球進黑洞

題意:

給定一個長度為 n 的序列,有 m 次操作:

操作有2種:

1. 區間求和,即輸入l,r,輸出\sum_{i=l}^{r}a[i] .

2. 區間異或,即輸入l,r,k,對於l\leq i\leq r,將 a[i] 變為 a[i]\oplus k .

資料範圍:

1\leq n,m\leq 10^{5}0\leq a[i],k\leq 10^{5}

思路:

我們無法得到一個區間更新的公式來做到區間異或,但異或是2進位制上的操作,我們可以想到對一個數二進位制的每一位來進行操作。因為 0\leq a[i],k\leq 10^{5} ,所以二進位制位數<20位,因此我們可以開20顆線段樹,第 i 顆線段樹來維護二進位制第 i 位上 1 的個數。

由於0異或上任何數 = 任何數本身,所以如果要異或的 k 的二進位制第 i 位上為 0,那麼這位就不需要考慮了。

此外,1異或上0=1,1異或上1=0 。也就是說,異或 1 能把 0 變成 1,把 1 變成 0 。所以如果要異或的 k 的二進位制第 i 位上為 1,區間更新的時候區間[l,r]中更新後 1 的個數就等以區間長度(r-l+1)減去更新前 1 的個數。(也就是更新後 1 的個數 = 更新前 0 的個數)那麼現在區間更新的公式已經得到了。

最後查詢的時候,區間[l,r]求和就等於區間[l,r]每一位上 1 的個數乘以當前二進位制位所代表的數值。

Code:

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;

const int MAX = 1e5+10;

typedef struct{
    ll sum,lazy;
}Point;

int n,m;
ll a[MAX];
Point tree[25][MAX<<2];

void PushUp(int id,int root)
{
    tree[id][root].sum=tree[id][root<<1].sum+tree[id][root<<1|1].sum;
}

void PushDown(int id,int root,int l,int r)
{
    if(tree[id][root].lazy==1){
        tree[id][root].lazy=0;
        tree[id][root<<1].lazy^=1;
        tree[id][root<<1|1].lazy^=1;
        int mid = (l+r)>>1;
        tree[id][root<<1].sum=(mid-l+1)-tree[id][root<<1].sum;
        tree[id][root<<1|1].sum=(r-mid)-tree[id][root<<1|1].sum;
    }
}

void build(int id,int l,int r,int root)
{
    if(l==r){
        tree[id][root].sum=((a[l]>>id)&1);
        tree[id][root].lazy=0;
        return;
    }
    int mid = (l+r)>>1;
    build(id,l,mid,root<<1);
    build(id,mid+1,r,root<<1|1);
    PushUp(id,root);
    return;
}

void update(int id,int L,int R,int l,int r,int root)
{
    if(L<=l&&r<=R){
        tree[id][root].sum=(r-l+1)-tree[id][root].sum;
        tree[id][root].lazy^=1;
        return;
    }
    int mid = (l+r)>>1;
    PushDown(id,root,l,r);
    if(L<=mid)  update(id,L,R,l,mid,root<<1);
    if(R>mid)   update(id,L,R,mid+1,r,root<<1|1);
    PushUp(id,root);
}

ll query(int id,int L,int R,int l,int r,int root)
{
    if(L<=l&&r<=R){
        return  tree[id][root].sum;
    }
    int mid = (l+r)>>1;
    PushDown(id,root,l,r);
    ll ans=0;
    if(L<=mid)  ans+=query(id,L,R,l,mid,root<<1);
    if(R>mid)   ans+=query(id,L,R,mid+1,r,root<<1|1);
    return ans;
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%lld",&a[i]);
    for(int i=0;i<=20;i++){
        build(i,1,n,1);
    }
    while(m--)
    {
        ll op,l,r,k;
        scanf("%lld",&op);
        if(op==1){
            scanf("%lld%lld",&l,&r);
            ll ans=0;
            for(int i=0;i<=20;i++){
                ans+=1ll*query(i,l,r,1,n,1)*(1ll<<i);//注意1<<i會爆int
            }
            printf("%lld\n",ans);
        }
        else{
            scanf("%lld%lld%lld",&l,&r,&k);
            for(int i=0;i<=20;i++){
                if((k>>i)&1){
                    update(i,l,r,1,n,1);
                }
            }
        }
    }
    return 0;
}