1. 程式人生 > >hdu 2665 Kth number(劃分樹模板題)

hdu 2665 Kth number(劃分樹模板題)

題意:給定一個長度為n的序列,進行m次查詢,求出區間[l,r]中的第k大值。

思路:劃分樹模板題。上學的時候做過這道題,當時看了下劃分樹的講解,看得很頭大,然後就一直放著了。十一回家的時候在高鐵上沒什麼事情,就重新學習了一遍劃分樹。

劃分樹是通過模擬快速排序,記錄快速排序的過程。因為快速排序本質上就是講一個很大的序列劃分成一段一段的小序列分別排序,所以可以將這個排序的過程給記錄下來,然後根據排序的過程,逐步縮小查詢區間。

思路講起來很簡單,但是實現的時候很麻煩。首先是區間是怎麼縮小的,因為每次[l,r]區間內的元素都會根據每次排序的座標值劃分為兩個部分,一部分在左子序列,一部分在右子序列,第k大值一定在其中某一邊,所以可以排除另一邊,然後根據排除情況調整l,r和k的值,直到區間中只剩下一個元素;其次是怎麼保證樹的深度,因為在最惡劣情況下快速排序的複雜度是O(n^2),樹的深度為n,為了避免這種情況,我們可以先對原序列排序,反正我們需要的是排序的過程,至於排序的結果並不重要,我們可以通過排序的結果倒推出每次切割序列最完美的座標值,保證樹的深度為log2(n);最後是區間縮小的公式,開始的時候我以為這部分是最容易實現的,拿著筆畫畫算算推出公式就行了,但是實際上這個公式我推了很多遍到最後推出來的還是錯的,還是根據測試資料不停地調整最後才AC掉了題目。

為了保證查詢的效率,在編碼的過程中還需要很多小技巧,具體可以看程式碼。

在學習劃分樹的過程中參考了百度百科。

最近工作略微輕鬆,可以有時間做一些想做的事情,所以花了比較多的時間看書和學習一些以前想學但是沒騰出手來學的東西。

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define N 100005
#define M 20
int n,m;
int origin[N],sort[N],sort_step[M][N],move_to_left[M][N];
int cmp(const void *a,const void *b)
{
    return *(int *)a-*(int *)b;
}
void Build(int rank,int l,int r)
{
    if(l==r)
    {
        sort_step[rank][l]=sort_step[rank-1][l];
        return ;
    }
    int mid=(l+r)/2;
    int left_index=l,right_index=mid+1;
    int left_seats=mid-l+1;
    for(int i=l;i<=mid;i++)
        if(sort[i]<sort[mid]) left_seats--;
    for(int i=l;i<=r;i++)
    {
        if(i==l) move_to_left[rank][i]=0;
        else move_to_left[rank][i]=move_to_left[rank][i-1];
        if(sort_step[rank-1][i]<sort[mid]) sort_step[rank][left_index++]=sort_step[rank-1][i],move_to_left[rank][i]++;
        else if(sort_step[rank-1][i]>sort[mid]) sort_step[rank][right_index++]=sort_step[rank-1][i];
        else
        {
            if(left_seats) left_seats--,sort_step[rank][left_index++]=sort_step[rank-1][i],move_to_left[rank][i]++;
            else sort_step[rank][right_index++]=sort_step[rank-1][i];
        }
    }
    Build(rank+1,l,mid);
    Build(rank+1,mid+1,r);
    return ;
}
int Query(int rank,int l,int r,int query_l,int query_r,int k)
{
    if(l==r) return sort_step[rank][l];
    int mid=(l+r)/2;
    int move_to_left_l,move_to_left_seg;
    if(query_l==l) move_to_left_l=0,move_to_left_seg=move_to_left[rank][query_r];
    else move_to_left_l=move_to_left[rank][query_l-1],move_to_left_seg=move_to_left[rank][query_r]-move_to_left[rank][query_l-1];
    if(k<=move_to_left_seg) return Query(rank+1,l,mid,l+move_to_left_l,l+move_to_left[rank][query_r]-1,k);
    else return Query(rank+1,mid+1,r,move_to_left[rank][r]+query_l-move_to_left_l,r-((r-query_r)-(move_to_left[rank][r]-move_to_left[rank][query_r])),k-move_to_left_seg);
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&origin[i]);
            sort[i]=sort_step[0][i]=origin[i];
        }
        qsort(sort+1,n,sizeof(sort[0]),cmp);
        Build(1,1,n);
        while(m--)
        {
            int l,r,k;
            scanf("%d%d%d",&l,&r,&k);
            printf("%d\n",Query(1,1,n,l,r,k));
        }
    }
    return 0;
}


相關推薦

hdu 2665 Kth number劃分模板

題意:給定一個長度為n的序列,進行m次查詢,求出區間[l,r]中的第k大值。 思路:劃分樹模板題。上學的時候做過這道題,當時看了下劃分樹的講解,看得很頭大,然後就一直放著了。十一回家的時候在高鐵上沒什麼事情,就重新學習了一遍劃分樹。 劃分樹是通過模擬快速排序,記錄快速排序的

hdu 2665 Kth number劃分

first con build 這一 cst second class stream tom Kth number Time Limit: 15000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Oth

[HDU 2665] Kth number 主席入門

HDU - 2665 靜態區間第 k大 這道題有很多種比主席樹簡單了一萬倍的演算法 不過作為主席樹入門還是很合適的 orz 這題的具體做法就是,先離散化值,在建立權值線段樹 從左到右掃一遍陣列,對第 i個數,在 A[i]的位置 +1 然後

HDU 2665 Kth number主席靜態區間第K大題解

可持久化 unique algorithm using 主席樹 可持久化線段樹 long spa 靜態區 題意:問你區間第k大是誰 思路:主席樹就是可持久化線段樹,他是由多個歷史版本的權值線段樹(不是普通線段樹)組成的。 具體可以看q學姐的B站視頻 代碼:

hdu 2665 Kth number函式化線段

題意:給定一個序列,問區間內第K大的數是多少。 #include<cstdio> #include<iostream> #include<algorithm> #include<map> using namespace st

HDU2665 Kth number歸併模板

Kth number 傳送門1傳送門2 Give you a sequence and ask you the kth big number of a inteval. Input The

HDU】2665Kth number劃分

只是為了存模板~ #include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int maxn = 100005; int n,m;

Kth number劃分

The first line is the number of the test cases. For each test case, the first line contain two integer n and m (n, m <= 100000), indicates the number of

HDU 1166 敵兵佈陣線段模板

敵兵佈陣 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 127082    Accepted Submissi

HDU 2222 Keywords SearchAC自動機模板

stack uil empty cst keywords cto ble ont max http://acm.hdu.edu.cn/showproblem.php?pid=2222 題意:給出多個單詞,最後再給出一個模式串,求在該模式串中包含了多少個單詞。 思路

bzoj1012線段模板

題目讀了三遍才讀懂,對於蒟蒻來說,看上去很難得樣子,實際就是線段樹的單點更新,然後求區間最值,無奈之前還想著怎麼建樹插進去。。結果RE n次,感覺自己水的一匹,唉~ 做題效率極低。。。。 真是被自己蠢哭了 #include<iostream> #i

HDU 2544 最短路dijkstra演算法模板

  Problem Description 在每年的校賽裡,所有進入決賽的同學都會獲得一件很漂亮的t-shirt。但是每當我們的工作人員把上百件的衣服從商店運回到賽場的時候,卻是非常累的!所以現在他們想要尋找最短的從商店到賽場的路線,你可以幫助他們嗎?

HDU 2665.Kth number-無修改區間第K小-可持久化線段(主席)模板

sort ota nbsp ani show 去重 第k小 math urn Kth number Time Limit: 15000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Oth

劃分基礎 —— HDU 2665 Kth number

The first line is the number of the test cases.  For each test case, the first line contain two integer n and m (n, m <= 100000), indicates the number

hdu 2665 Kth number 劃分

求區間第k大元素的值, 看程式碼的註釋。 #include<cstring> #include<cstdio> #include<iostream> #include<algorithm> using namespace

hdu 2665 Kth number 主席

Kth number Time Limit: 15000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 12712&n

hdu 2665 Kth number

title truct big blank rom acc col ott scrip Time Limit: 15000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Sub

hdu Minimum Inversion Number線段求逆序數有關問題的一個小歸納

Minimum Inversion Number Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 4904  

2665 Kth number 靜態區間第k大

Give you a sequence and ask you the kth big number of a inteval. InputThe first line is the numbe

POJ 2104 K-th Number 劃分 / 主席

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