1. 程式人生 > >poj 2104 K-th Number 主席樹+超級詳細解釋

poj 2104 K-th Number 主席樹+超級詳細解釋

題目大意:給出一段數列,讓你求[L,R]區間內第幾大的數字!

在這裡先介紹一下主席樹!
如果想了解什麼是主席樹,就先要知道線段樹,主席樹就是n棵線段樹,因為線段樹只能維護最大值或者最小值,要想求出第二大的數字怎麼辦呢?兩顆線段樹唄!好,那麼第n大呢,就可以構造n棵線段樹,這樣的記憶體是顯然會爆掉的,那麼怎麼辦呢?因為每一次更新都是更新的是從葉子節點到根節點的一條路,路的長度大約是logn,如下圖紅色的為更新的時候變化的節點:
這裡寫圖片描述

當進行更新操作的時候,也就是新建一個線段樹,但是這可線段樹不會全部都建立起來,只把修改的節點建立一下就可以,如果節點不修改就可以用以前線段樹的節點指向當前新建的線段樹的節點,先看下圖然後在解釋:
這裡寫圖片描述


對於沒有修改的節點就直接指向新建的線段樹就可以了!
對於每一棵線段樹都是類似於下圖的樣子的:
這裡寫圖片描述
這棵樹表示數字1出現了三次,數字2出現了1次,數字三出現了一次,數字五出現了4次,數字6出現了三次;
用一個數組來表示就是sum[i]=j表示數字i出現了j次。
這樣在通過一般線段樹的pushdown操作就變為了我們熟悉的線段樹了!如下圖:
這裡寫圖片描述

下面據一個例子[1,1,2,1,5,5,5,5,6,6,6,3]解釋一下建立線段樹的全部過程:
首先是一棵空的線段樹
這裡寫圖片描述
順序的由第一個元素開始進入線段樹並且開始更新:
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述

這樣我們就可以知道不同狀態下的線段樹了,比如sum[i]當前節點對應的數字出現了幾次,對於[

L,R]這個區間我們就可以用sum[R]sum[L1]表示出如果這個區間大於k那麼就從他的左子樹接著找,否則從右子樹中找K(sum[R]sum[L1])這個多個就是第k大的數字!
這個基於的原理舉個例子就知道了我們知道區間[1,3,3,4,5]區間一共有5個元素那麼第5大的就是最後一個元素了!

AC程式碼

#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace
std; const int MAXN = 100010; const int N = MAXN*40; int n,m,q,tot; int T[MAXN],A[MAXN],t[MAXN]; int lson[N],rson[N],sum[N]; vector<int>V; int getid(int x) //離散化 { return lower_bound(V.begin(),V.end(),x)-V.begin()+1; } int build(int l,int r) //建立一棵空樹 { int rt = tot++; sum[rt] = 0; if(l!=r){ int mid=(l+r)>>1; lson[rt] = build(l,mid); rson[rt] = build(mid+1,r); } return rt; } int update(int rt,int pos) //把陣列中的元素一次加入新的線段樹中 { int nrt = tot++; int tmp = nrt; sum[nrt] = sum[rt]+1; int l=1,r=m; while(l<r) { int mid = (l+r)>>1; if(pos<=mid) { lson[nrt] = tot++; rson[nrt] = rson[rt]; nrt = lson[nrt]; rt = lson[rt]; r = mid; }else { rson[nrt] = tot++; lson[nrt] = lson[rt]; nrt = rson[nrt]; rt = rson[rt]; l=mid+1; } sum[nrt] = sum[rt]+1; } return tmp; } int query(int lrt,int rrt,int k) { int l=1,r=m; while(l<r) { int mid = (l+r)>>1; int cnt = sum[lson[rrt]] - sum[lson[lrt]]; if(cnt>=k) { r = mid; lrt = lson[lrt]; rrt = lson[rrt]; } else { l = mid+1; k-=cnt; lrt = rson[lrt]; rrt = rson[rrt]; } } return l; } int main() {//freopen("in.txt","r",stdin); scanf("%d%d",&n,&q);tot=0; for(int i=1;i<=n;i++) { scanf("%d",&A[i]); V.push_back(A[i]); } sort(V.begin(),V.end()); V.erase(unique(V.begin(),V.end()),V.end()); m=V.size(); T[0] = build(1,m); for(int i=1;i<=n;i++) { T[i] = update(T[i-1],getid(A[i])); } while(q--) { int x,y,k; scanf("%d%d%d",&x,&y,&k); printf("%d\n",V[query(T[x-1],T[y],k)-1]); } return 0; }