整體二分
阿新 • • 發佈:2020-09-06
整體二分
今天剛透徹了整體二分,寫篇blog;
由於整體二分比較難以口述,來道例題講解
靜態區間第k小
同時也是主席樹模板題,可是主席樹模板怎麼能用主席樹寫呢
往下看,不會主席樹的也可以水過這道紫體題了;
首先設A序列表示表示給定序列;
所有詢問的答案一定在MINA~MAXA之間;
對MINA~MAXA進行二分答案;
二分答案一個詢問想必大家都會,單數這道題時間不允許;
考慮對整體進行二分,對於所有詢問二分;
也就是對詢問進行分類,考慮二分出的答案mid;
用樹狀陣列維護小於等於mid的個數;
對於每個區間ask(r)-ask(l-1)可以知道在這個詢問區間裡小於等於mid的個數;
就可以把所有詢問分為兩類,設num為l~r裡小於等於mid的個數;
第一類:num<=k 第二類num>k;
考慮第一類詢問的答案一定在L~mid之間;
第二類詢問的答案一定在mid+1~R之間;
L,R表示當前的答案區間;
那麼就可以把詢問分成兩類在分別將行分治
知道L==R為止,L就是答案;
注意A序列也要分開;
考慮對於第一類詢問,A序列中>mid的值對這些區間沒有影響
所以可以直接捨棄;
對於第二類詢問,它們的答案一定在mid+1~r之間
所以只考慮>mid的值;
#include<iostream> #include<cstdio> using namespace std; const int N=2e5+7; const int inf=1e9; struct node{ int op,x,y,z; }q[N<<1],ql[N<<1],qr[N<<1]; int n,m,cnt; int ans[N],t[N]; int lowbit(int x){ return x&-x; } void change(int x,int val){ for(;x<=N-7;x+=lowbit(x)){ t[x]+=val; } } int ask(int x){ int res=0; for(;x;x-=lowbit(x)){ res+=t[x]; } return res; } void work(int L,int R,int l,int r){ if(l>r) return; if(L==R){ for(int i=l;i<=r;i++){ if(q[i].op>0) ans[q[i].op]=L; } return; } int mid=(L+R)>>1; int tl=0,tr=0; for(int i=l;i<=r;i++){ if(q[i].op==0){//屬於A序列 if(q[i].y<=mid) change(q[i].x,1),ql[++tl]=q[i]; else qr[++tr]=q[i]; } else {//屬於詢問區間 int tt=ask(q[i].y)-ask(q[i].x-1); if(tt>=q[i].z) ql[++tl]=q[i]; else { q[i].z-=tt;//減去tt; qr[++tr]=q[i]; } } } for(int i=r;i>=l;i--){ if(q[i].op==0&&q[i].y<=mid) change(q[i].x,-1);//清空樹狀陣列; } for(int i=1;i<=tl;i++) q[l+i-1]=ql[i]; for(int i=1;i<=tr;i++) q[l+tl+i-1]=qr[i]; work(L,mid,l,l+tl-1); work(mid+1,R,l+tl,r); } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ scanf("%d",&q[++cnt].y); q[cnt].op=0;q[cnt].x=i;//op分類 } for(int i=1;i<=m;i++){ int x,y,z; scanf("%d%d%d",&x,&y,&z); q[++cnt].x=x; q[cnt].y=y; q[cnt].z=z; q[cnt].op=i; } work(-inf,inf,1,cnt); for(int i=1;i<=m;i++) cout<<ans[i]<<"\n"; }