1. 程式人生 > >K-th Number (劃分樹學習)

K-th Number (劃分樹學習)

剛開始的樸素想法,也就是老師上課說的部分快排。

但做了以後發現並沒那麼簡單,首先是qsort中,以前寫的都有等號,但不確定到底是哪個位置

於是改為無等號。

笨辦法,部分需排的由a放入b

#include <stdio.h>
int a[200010];
int b[100000];
int knumber;
void qqsort(int low,int high)
{
    int l=low,h=high;
    b[0]=b[l];

    while(l<h)
    {
        while( l<h && b[h]>=b[0] )h--;
        b[l]=b[h];
        while( l<h && b[l]<=b[0] )l++;
        b[h]=b[l];
    }
    b[l]=b[0];
    if(l==knumber)
        return ;
    else if(l>knumber)
        qqsort(low ,h-1);
    else
        qqsort(l+1,high);
    
}

int main()
{
    int i,j,n,m,num;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)
        scanf("%d",&a[i]);
    while(m--)
    {
        scanf("%d%d%d",&i,&j,&knumber);
        for(num=1;num+i-1<=j;num++)
            b[num]=a[i+num-1];
        qqsort(1,num-1);
        printf("%d\n",b[knumber]);
            
    }
    return 0;
} 

因此需要劃分樹。

參考:

http://yueyue1105.blog.163.com/blog/static/431117682010716111425892/

http://www.cnblogs.com/pony1993/archive/2012/07/17/2594544.html

劃分樹是一種基於線段樹的資料結構。主要用於快速求出(在log(n)的時間複雜度內)序列區間的第k大值 。

劃分樹和歸併樹都是用線段樹作為輔助的,原理是基於快排 和歸併排序 的。

劃分樹的建樹過程基本就是模擬快排過程,取一個已經排過序的區間中值,然後把小於中值的點放左邊,大於的放右邊。並且記錄d層第i個數之前(包括i)小於中值的放在左邊的數。

假設要在區間[l,r]中查詢第k大元素,t為當前節點,lch,rch為左右孩子,left,mid為節點t左邊界和中間點。
1、sum[r]-sum[l-1]>=k,查詢lch[t],區間對應為[ left+sum[l-1] , left+sum[r]-1 ]
2、sum[r]-sum[l-1]<k,查詢rch[t],區間對應為[ mid+1+l-left-sum[l-1] , mid+1+r-left-sum[r] ]

模版:

/*
HDU  2665 Kth number
劃分樹


*/


#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;

const int MAXN=100010;
int tree[30][MAXN];//表示每層每個位置的值
int sorted[MAXN];//已經排序的數
int toleft[30][MAXN];//toleft[p][i]表示第i層從1到i有多少個數分入左邊

void build(int l,int r,int dep)
{
    if(l==r)return;
    int mid=(l+r)>>1;
    int same=mid-l+1;//表示等於中間值而且被分入左邊的個數
    for(int i=l;i<=r;i++)
      if(tree[dep][i]<sorted[mid])//先假設左邊的(mid - l + 1)個數都等於as[mid],然後把實際上小於as[mid]的減去
         same--;//為了保持樹的平衡,不然若是很多相同的則會全部偏向一邊
    int lpos=l;
    int rpos=mid+1;
    for(int i=l;i<=r;i++)
    {
        if(tree[dep][i]<sorted[mid])//比中間的數小,分入左邊
             tree[dep+1][lpos++]=tree[dep][i];
        else if(tree[dep][i]==sorted[mid]&&same>0)
        {
            tree[dep+1][lpos++]=tree[dep][i];
            same--;
        }
        else  //比中間值大分入右邊
            tree[dep+1][rpos++]=tree[dep][i];
        toleft[dep][i]=toleft[dep][l-1]+lpos-l;//從1到i放左邊的個數

    }
    build(l,mid,dep+1);
    build(mid+1,r,dep+1);

}


//查詢區間第k大的數,[L,R]是大區間,[l,r]是要查詢的小區間
//假設要在區間[l,r]中查詢第k大元素,t為當前節點,lch,rch為左右孩子,left,mid為節點t左邊界和中間點
//sum[r]-sum[l-1]>=k,查詢lch[t],區間對應為[ left+sum[l-1], left+sum[r]-1 ]
//sum[r]-sum[l-1]<k,查詢rch[t],區間對應為[ mid+1+l-left-sum[l-1], mid+1+r-left-sum[r] ]

int query(int L,int R,int l,int r,int dep,int k)
{
    if(l==r)return tree[dep][l];
    int mid=(L+R)>>1;
    int cnt=toleft[dep][r]-toleft[dep][l-1];//[l,r]中位於左邊的個數
    if(cnt>=k)
    {
        //L+要查詢的區間前被放在左邊的個數
        int newl=L+toleft[dep][l-1]-toleft[dep][L-1];
        //左端點加上查詢區間會被放在左邊的個數
        int newr=newl+cnt-1;
        return query(L,mid,newl,newr,dep+1,k);
    }
    else
    {
         int newr=r+toleft[dep][R]-toleft[dep][r];
         int newl=newr-(r-l-cnt);
         return query(mid+1,R,newl,newr,dep+1,k-cnt);
    }
}


int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int T;
    int n,m;
    int s,t,k;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        memset(tree,0,sizeof(tree));//這個必須
        for(int i=1;i<=n;i++)//從1開始
        {
            scanf("%d",&tree[0][i]);
            sorted[i]=tree[0][i];
        }
        sort(sorted+1,sorted+n+1);
        build(1,n,0);
        while(m--)
        {
            scanf("%d%d%d",&s,&t,&k);
            printf("%d\n",query(1,n,s,t,0,k));
        }
    }
    return 0;
}


相關推薦

K-th Number (劃分學習)

剛開始的樸素想法,也就是老師上課說的部分快排。 但做了以後發現並沒那麼簡單,首先是qsort中,以前寫的都有等號,但不確定到底是哪個位置 於是改為無等號。 笨辦法,部分需排的由a放入b #include <stdio.h> int a[200010]; int

POJ2104 K-th Number —— 劃分(模板題)

K-th Number Time Limit: 20000MS Memory Limit: 65536K Total Submissions: 59744 Accepted: 20847 Case Time Limit: 2000MS Descriptio

POJ 2104 K-th Number (主席)

std +++ esp space ctype == string uniq upd 題意:給定一個序列,然後有 q 個詢問,每次詢問 l - r 區間內的第 k 大的值。 析:很明顯的主席樹,而且還是裸的主席樹,先進行離散化,然後用主席樹進行查詢就好。 代碼如下: #p

Poj 2104 K-th Number 主席模版題

OS tdi pda sig signed begin ostream air color 題意:離線詢問[l,r]區間第k大 題解:模版題,入門題 #include <iostream> #include <cstdio> #inclu

【poj2104】K-th Number 歸併

說起來這是第二次A這個題了23333 第一次是分塊,戳這裡 歸併樹比分塊打起來爽,速度還快,簡直好評 思想:線段樹套陣列【霧。線段樹每個節點儲存它所表示的區間的有序數字排列。實現就是在歸併排序的過程中儲存歸併排序的過程。蠻好打的。 並且除錯什麼的也沒出什

POJ 2104 K-th Number 主席模板題

題意:給定一個長度為n的無序陣列,然後有m組詢問l r k,求區間[l,r]中的第k大數 思路:以前用劃分樹做過,現在學習主席樹,模板題。個人對主席樹的理解:就是把線段樹更新過程中所有的歷史狀態記錄下來,例如更新m次的話,那麼就會有m種狀態,直接建m棵線段樹的話,時間和空

POJ 2104 K-th Number [主席入門]【資料結構】

題目連結:http://poj.org/problem?id=2104 ———————————————————————————————————————————————— K-th Number Time Limit: 20000MS Memory

POJ 2104 K-th Number 主席(求區間第k大)

主席書資料 題意:給出n個數,m次詢問,[x,y]內第k小的數時多少?n<=1e5,m<=5000 主席樹:對原序列的每個字首i都建立一個線段樹 維護值域[l,r]中的每個數,在字首i的

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

題目大意:給出一段數列,讓你求[L,R]區間內第幾大的數字! 在這裡先介紹一下主席樹! 如果想了解什麼是主席樹,就先要知道線段樹,主席樹就是n棵線段樹,因為線段樹只能維護最大值或者最小值,要想求出第二大的數字怎麼辦呢?兩顆線段樹唄!好,那麼第n大呢,就可

POJ 2104 K-th Number(區間第k大數)(平方切割,歸並劃分

ac代碼 deb rank turn tracking line 查看 div 能夠 題目鏈接: http://poj.org/problem?id=2104 解題思路: 由於查詢的個數m非常大。樸素的求法無法在規定時間內求解。因此應該選用合理的方式維護數據來做到高效

K-th Number POJ - 2104 (主席 學習詳解)

https://cn.vjudge.net/problem/POJ-2104 題意 給你N個數 嗎、M次查詢,每次查詢給你 IJK 問第I個數到第J個數中第K大 思路 字典樹,每新增一個數都建立一棵線段樹,J和I 做減法就可以的到這個區間的線段樹 #include <c

劃分K-th Number

/* 此題採用劃分歸併樹進行計算 * 題號:poj 2104 */ #include #include #include #include #include #define MAXN 100010 using namespace std; int num[MAXN]; // 用於儲存輸入的陣列

POJ 2104 K-th Number劃分 / 主席

Description You are working for Macrohard company in data structures department. After failing yo

POJ 2104 K-th Number劃分,主席寫過了,這次是整體二分解法 )

還是先描述一下題意: 給出一個長度為n的數列,m次詢問區間內的第k大數 對劃分樹,主席樹和整體二分通過這題做了一下比較 劃分樹  1000ms+ 主席樹 2000ms+ 整體二分 1500ms+ 整體二分介於兩者之前,對於這題複雜度約莫是O( (n+m)log(n+m)l

POJ 2104 K-th Number(主席

ber sca first n) 次數 example == scan sorted K-th Number Time Limit: 20000MS Memory Limit: 65536K Total Submissions: 5742

【POJ 2104】【主席模板題】K-th Number

題意:       靜態詢問區間第K大問題。給出一個數組,然後多次詢問某一區間第K大數是多少。   思路:       典型的主席樹模板題。       所以就大致講一下靜態主席樹

A - K-th Number POJ - 2104 -主席第一彈-第K大的數

感謝:http://www.cnblogs.com/zyf0163/p/4749042.html https://blog.csdn.net/qq_24451605/article/details/49031123 裸題多次查詢給定區間L—R內第K大的數 #include&

HDOJ2665 Kth number --- 劃分求區間第k小數

Problem Description Give you a sequence and ask you the kth big number of a inteval. Input The first line is the number of the test c

歸併(POJ 2104 K-th Number)

        在求解區間第k個數的問題,除了劃分樹以外我們還可以使用另一種高效的方法 ------ 歸併樹。 1、演算法描述         所謂歸併樹,就是利用線段樹的建樹過程,將歸併排序的過程儲存。(不會線段樹:here,不會歸併排序:here)。在說明歸併樹之前我們

poj 2104 K-th Number (主席入門模板題)

摘抄了一段主席樹的解釋:所謂主席樹呢,就是對原來的數列[1..n]的每一個字首[1..i](1≤i≤n)建立一棵線段樹,線段樹的每一個節點存某個字首[1..i]中屬於區間[L..R]的數一共有多少個(比如根節點是[1..n],一共i個數,sum[root]