紅球進黑洞(線段樹區間異或+區間求和)
阿新 • • 發佈:2018-12-22
題目連結:
題意:
給定一個長度為 n 的序列,有 m 次操作:
操作有2種:
1. 區間求和,即輸入l,r,輸出 .
2. 區間異或,即輸入l,r,k,對於,將 a[i] 變為 .
資料範圍:
,
思路:
我們無法得到一個區間更新的公式來做到區間異或,但異或是2進位制上的操作,我們可以想到對一個數二進位制的每一位來進行操作。因為 ,所以二進位制位數<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; }