1. 程式人生 > >51nod 1175 區間第k大 整體二分

51nod 1175 區間第k大 整體二分

題意:

一個長度為N的整數序列,編號0 - N - 1。進行Q次查詢,查詢編號i至j的所有數中,第K大的數是多少。

分析:

  僅僅就是一道整體二分的入門題而已,沒聽說過整體二分?

  其實就是一個分治的函式,但是呢,我所理解的,這是一個只分不治的過程。為什麼?因為我們把數值域和操作域經過若干次劃分劃到最後,當數值域固定到一個值時,如果存在對應的操作域(此時應該是詢問域)的結果就已經是這個值了,所以並不需要合併(治)。

  上面這段話可能被我複雜化了?那我們簡單說。(以下是求區間第k小的步驟)

  這樣的題一般都是給出一個序列,詢問區間第k大/小。我們把這個序列看做新增一個數 a[i] 在位置 i 的操作。詢問也是操作。

  然後就是一個遞迴函式,不斷二分數值的範圍(數值域),每次將屬於 [ l,mid ] 這個範圍的新增操作以原位置在樹狀陣列中加1,直接劃到左部(操作域),將 [ mid+1,r ]  直接劃到右部(不碰樹狀陣列),對於詢問呢,假如在樹狀陣列中查詢到的,位於這個區間內的1的個數小於k,證明這個區間內,左部新增的數(不大於mid的)小於k個,所以這個詢問的答案一定在右部新增的數中(也就是一定比mid要大),因此,假如這個區間內的數在左部有x個,我們需要在右部求出這個區間的第k-x小的數即可,即將次詢問的k減去x,劃分到右部,反之,若我們在樹狀陣列上查出來的數大於等於k,證明答案會在左部被求出,直接劃分到左部即可!

  我們無意間就維護了兩個單調性,一個是加入順序的單調性,另一個是數值的單調性,這二者的有機結合,恰好就是整體二分的優勢,可以讓其解決很多比較複雜,多處有二分性的題目。

  上面的解釋可能大家看了都會很迷茫,但是研究程式碼會讓你更明白。

程式碼:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<algorithm>
 6 using namespace std;
 7
const int inf=0x3f3f3f3f,MAXN=200050; 8 struct node{ 9 int x,y,z,id,type; 10 }a[MAXN],al[MAXN],ar[MAXN]; 11 int n,m,ans[MAXN]; 12 struct bit{ 13 int a[MAXN]; 14 void add(int x,int y) 15 {for(;x<=n;x+=x&(-x)) a[x]+=y;} 16 int query(int x){ 17 int ans=0;for(;x;x-=x&(-x)) ans+=a[x]; 18 return ans;} 19 }t; 20 void solve(int l,int r,int ql,int qr){ 21 if(ql>qr||l>r) return ;//l和r是數值域 22 if(l==r){//ql,lr是操作域 23 for(int i=ql;i<=qr;i++) 24 ans[a[i].id]=l;return ; 25 } int mid=(l+r)>>1,lal=0,lar=0; 26 for(int i=ql;i<=qr;i++) 27 if(a[i].type){ 28 if(a[i].y<=mid){ 29 t.add(a[i].x,a[i].z); 30 al[++lal]=a[i]; 31 } else ar[++lar]=a[i]; 32 } else{ int tmp; 33 tmp=t.query(a[i].y)-t.query(a[i].x-1); 34 if(tmp>=a[i].z) al[++lal]=a[i]; 35 else ar[++lar]=a[i],ar[lar].z-=tmp; 36 } for(int i=1;i<=lal;i++) 37 if(al[i].type==1) t.add(al[i].x,-al[i].z); 38 for(int i=1,j=ql;i<=lal;i++,j++) 39 a[j]=al[i]; 40 for(int i=1,j=ql+lal;i<=lar;i++,j++) 41 a[j]=ar[i];solve(l,mid,ql,ql+lal-1); 42 solve(mid+1,r,ql+lal,qr);return ; 43 } int main(){ 44 scanf("%d",&n); 45 for(int i=1;i<=n;i++) 46 scanf("%d",&a[i].y),a[i].x=i,a[i].y=-a[i].y, 47 a[i].z=1,a[i].type=1; 48 scanf("%d",&m); 49 for(int i=1,j=n+1;i<=m;i++,j++) 50 scanf("%d%d%d",&a[j].x,&a[j].y,&a[j].z),a[j].x++,a[j].y++, 51 a[j].id=i,a[j].type=0; 52 solve(-inf,inf,1,n+m); 53 for(int i=1;i<=m;i++) 54 printf("%d\n",-ans[i]); return 0; 55 }
整體二分