標籤:shu 格式 支援 運算 build 資料 typedef 樹節點 沒有
題目連結:https://www.nowcoder.com/acm/contest/211/E
題目描述
請實現一個資料結構支援以下操作:區間迴圈左右移,區間與,區間或,區間求和。
輸入描述:
第一行n,q表示數列長度及操作次數。
第二行n個數表示初始序列。
接下來q行表示操作。
操作格式如下:
一行表示一個操作。所有操作形如 opt l r v。
opt=1 表示將區間[l,r]迴圈右移v位。
opt=2 表示將區間[l,r]迴圈左移v位。
opt=3 表示將區間[l,r]按位或上v。
opt=4 表示將區間[l,r]按位與上v。
opt=5 詢問區間[l,r]的和。
保證opt=1或2時 1 ≤ v ≤ 20
注意:為了最佳化你的做題體驗,操作5也會輸入一個v,但是是沒有意義的。
注意:迴圈左右移在20個二進位位的意義下進行
輸出描述:
對於每個opt=5的操作,輸出一個數表示答案。
輸入
10 10
10112 23536 1305 7072 12730 29518 12315 3459 12435 29055
4 5 10 12373
2 1 6 7
5 4 10 24895
1 1 4 8
5 3 7 7767
5 7 9 6127
4 2 8 30971
5 4 10 2663
1 7 10 1
1 2 9 5
輸出
2001530
1600111
24611
49482
備忘:
1 ≤ N,Q ≤ 2*10^5 且 0 ≤ ai < 2^20
一些說明:
1. 對於00000000000000000101,右移一位後會變成10000000000000000010
2. 不是區間位移,是區間中的每一個數的二進位位的位移
題解:
(說實話這道題其實不難的,會線段樹就應該想到怎麼做的,可能是我腦子太笨了吧,比賽的時候想不到怎麼做。)
考慮到只有二進位下只有 $20$ 位,而且修改操作都是按位與或者按位或,所以可以將 $20$ 位拆開來看。
然後就簡單了呀:
對 $01$ 序列中某一個區間,全部與上 $x(x=0,1)$,那麼 $x = 1$ 時不變, $x = 0$ 時全 $0$。
對 $01$ 序列中某一個區間,全部或上 $x(x=0,1)$,那麼 $x = 0$ 時不變, $x = 1$ 時全 $1$。(這兩種操作可以歸結為直接把一個常見的線段樹區間覆蓋操作)
迴圈平移就是在 $20$ 棵平衡樹進行資料的迴圈平移,向左向右迴圈平移都可以全部歸結為向右平移,範圍為 $(0,19)$,這個就是把每個線段樹節點裡的二十位的01序列重新排一下即可。
求和,我們線段樹每個節點都有一個 $val[20]$ 用以為維護某一位上,當前區間內所有數字在這一位上合起來總共出現了多少個 $1$,也就是說這個 $val[20]$ 相當於一個算加法但還沒來得及進位的位元,這樣的位元轉成十進位和普通位元轉十進位是一樣的。
AC代碼:
#include<bits/stdc++.h>using namespace std;typedef long long ll;const int maxn=2e5+10;int n,q,a[maxn];/********************************* Segment Tree - st *********************************/int tmp[20];struct Node{ int l,r; int val[20]; int lazy[20],shft; void upd_shft(int x) { x=(x+20)%20; shft=(shft+x)%20; for(int i=0;i<x;i++) tmp[i]=lazy[i]; for(int i=0;i<20-x;i++) lazy[i]=lazy[i+x]; for(int i=0;i<x;i++) lazy[i+20-x]=tmp[i]; for(int i=0;i<x;i++) tmp[i]=val[i]; for(int i=0;i<20-x;i++) val[i]=val[i+x]; for(int i=0;i<x;i++) val[i+20-x]=tmp[i]; } void upd_lazy(int i,int x) { val[i]=(r-l+1)*x; lazy[i]=x; }}node[4*maxn];void pushdown(int root){ if(node[root].shft) { node[root*2].upd_shft(node[root].shft); node[root*2+1].upd_shft(node[root].shft); node[root].shft=0; } for(int i=0;i<20;i++) { if(node[root].lazy[i]!=-1) { node[root*2].upd_lazy(i,node[root].lazy[i]); node[root*2+1].upd_lazy(i,node[root].lazy[i]); node[root].lazy[i]=-1; } }}void pushup(int root){ for(int i=0;i<20;i++) node[root].val[i]=node[root*2].val[i]+node[root*2+1].val[i];}void build(int root,int l,int r){ if(l>r) return; node[root].l=l, node[root].r=r; node[root].shft=0; for(int i=0;i<20;i++) node[root].lazy[i]=-1; if(l==r) { for(int i=0;i<20;i++) node[root].val[i]=(a[l]>>i)%2; } else { int mid=(l+r)/2; build(root*2,l,mid); build(root*2+1,mid+1,r); pushup(root); }}void update(int type,int root,int st,int ed,int x){ if(st<=node[root].l && node[root].r<=ed) { if(type==1) { node[root].upd_shft(x); } if(type==2) { for(int i=0,k;i<20;i++) { k=(x>>i)%2; if(!k) node[root].upd_lazy(i,0); } } if(type==3) { for(int i=0,k;i<20;i++) { k=(x>>i)%2; if(k) node[root].upd_lazy(i,1); } } } else { int mid=(node[root].l+node[root].r)/2; pushdown(root); if(st<=mid) update(type,root*2,st,ed,x); if(ed>mid) update(type,root*2+1,st,ed,x); pushup(root); }}ll query(int root,int st,int ed){ if(st<=node[root].l && node[root].r<=ed) { ll res=0; for(int i=0;i<20;i++) res+=(ll)node[root].val[i]*(1<<i); return res; } pushdown(root); int mid=(node[root].l+node[root].r)/2; ll res=0; if(st<=mid) res+=query(root*2,st,ed); if(ed>mid) res+=query(root*2+1,st,ed); return res;}/********************************* Segment Tree - ed *********************************/int main(){ scanf("%d%d",&n,&q); for(int i=1;i<=n;i++) scanf("%d",&a[i]); build(1,1,n); while(q--) { int op,l,r,v; scanf("%d%d%d%d",&op,&l,&r,&v); if(op==1) update(1,1,l,r,v); if(op==2) update(1,1,l,r,-v); if(op==3) update(3,1,l,r,v); if(op==4) update(2,1,l,r,v); if(op==5) printf("%lld\n",query(1,l,r)); }}
nowcoder 211E - 位元運算?位元運算! - [二進位線段樹][與或線段樹]