POJ 2104 K-th Number 【劃分樹】

題目來源:http://poj.org/problem?id=2104
在這裏插入圖片描述
★通過這個題來簡述一下劃分樹的算法,算是模板題了~


題意:

給一個長度爲n的序列,然後有m次詢問,每次給一個區間 輸出這個區間第k小的數


思路:

具體思路參考博客:https://blog.csdn.net/zxy_snow/article/details/6681086
我就說一些沒講到的細節

劃分樹的結構以及建樹:

劃分樹 結點是原序列,然後左右子結點是 以 排序後的原序列(數組f)的對應中間值f [ mid ]爲基準,小於等於它的按原序列的次序排在左邊(排滿mid-l+1個爲止,就是說有部分右結點會存在等於中間值的數) 其它排在右邊 ~ 然後子節點再往下重複操作遞歸
例如 序列 (1 5 2 3 6 4 7 3 0 0 ) 中間值爲 排序後的數組 (0 0 1 2 3 3 4 5 6 ) 中間值爲3
左結點 ( 1 2 3 0 0 )右結點(5 6 4 7 3)

// build代碼中 res加加減減就是爲了防止重複過多的數進入左結點

查詢:

num[ dep ] [ j ] 的意思就是 第dep層前j個有多少個屬於左結點,然後如果查找(l,r)第k大的數
我們可以先求出這個區間有多少小於中間值的數cnt int cnt=num[dep][y]-num[dep][x-1];
如果 cnt>=k 那說明我們要求的第k小肯定在這個左區間(l,mid) 裏面,不過 明顯可以縮小查詢的區間
如果 cnt<k 那說明這cnt個的數中不可能有我們要找的第k小,肯定要去 右區間(mid+1,r) 中找,而這時找的是 右區間第k-cnt小 的(這可以理解叭)

縮小區間:

不難發現,假設現在的父結點的總區間爲(l,r),而我們的查詢區間爲(x,y)
父結點的區間可以被分爲3塊 左(l,x-1) 查詢(x,y) 右(y+1,r)
假設現在cnt>=k,我們要遞歸到左結點,左結點的區間肯定也有3個塊
若設爲左(l,l1-1) 查詢(l1,r1) 右(r1+1,r ) 我們最後肯定查詢(l1,r1)
左結點的左區間的數肯定是來自於 父節點的左區間中小於中間值的數
左結點的左區間的數的個數就是num[dep][x-1]-num[dep][l-1],那麼l1就好求了,又知道查詢區間的長度,r1也很容易求出來
同理求右結點~並獻上一張醜圖
圖中區間1 2 3 分別是父節點的左 查詢 右 區間 ,中間的藍線分割了左右結點的區間

======
這裏簡要說明一下爲什麼int r2=y+num[dep][r]-num[dep][y]; 可能不是那麼直觀(我看了半天)
區間6+區間9=區間3=r-y 而 r2=r-區間9 —> r2=y+區間6 區間6就不用我說了吧
(區間x 代表的是區間長度)
在這裏插入圖片描述


代碼:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<stack>
#include<queue>
#include<map>
#include<vector>
#include<set>
#define ls k<<1,l,mid
#define rs k<<1|1,mid+1,r
#define r(x) read(x)
using namespace std;
typedef long long LL;
const int N=1e5+5;
const int M=1e3+5;
const int mod=1e9+7;
const int inf=0x7fffffff;
const double pi=acos(-1);
const double eps=1e-8;
template<class T>
inline void read(T &x)
{
    char c;x=1;
    while((c=getchar())<'0'||c>'9') if(c=='-') x=-1;
    T res=c-'0';
    while((c=getchar())>='0'&&c<='9') res=res*10+c-'0';
    x*=res;
}
int f[N];
int v[20][N];
int num[20][N];
void build(int l,int r,int dep)
{
    if(l==r) return ;
    int mid=(l+r)>>1;
    int res=mid-l+1;
    for(int i=l;i<=r;i++) if(v[dep][i]<f[mid]) res--;
    int l1=l,l2=mid+1;
    for(int i=l;i<=r;i++){
        if(v[dep][i]<f[mid]||(v[dep][i]==f[mid]&&res>0)){
            v[dep+1][l1++]=v[dep][i];
            if(v[dep][i]==f[mid]) res--;
        }
        else{
            v[dep+1][l2++]=v[dep][i];
        }
        num[dep][i]=num[dep][l-1]+l1-l;
    }
    build(l,mid,dep+1);
    build(mid+1,r,dep+1);
}
int query(int l,int r,int dep,int x,int y,int k)
{
//    cout<<l<<' '<<r<<endl;
    if(x==y) return v[dep][x];
    int mid=(l+r)>>1;
    int cnt=num[dep][y]-num[dep][x-1];
    if(cnt>=k){
        int l1=l+num[dep][x-1]-num[dep][l-1];
        int r1=l1+cnt-1;
        return query(l,mid,dep+1,l1,r1,k);
    }
    else{
        int r2=y+num[dep][r]-num[dep][y];
        int l2=r2-(y-x-cnt);
//        cout<<l2<<' '<<r2<<endl;
        return query(mid+1,r,dep+1,l2,r2,k-cnt);
    }
}
int main()
{
    int n,m;
    r(n); r(m);
    for(int i=1;i<=n;i++){
        r(f[i]);
        v[0][i]=f[i];
    }
    sort(f+1,f+n+1);
    build(1,n,0);
//    for(int i=0;i<=4;i++){
//        for(int j=1;j<=n;j++) cout<<num[i][j]<<' ';
//        cout<<endl;
//    }
    while(m--){
        int a,b,c;
        r(a); r(b); r(c);
        cout<<query(1,n,0,a,b,c)<<endl;
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章