對應 HDU 題目 :點擊打開鏈接
Kth number
Time Limit: 15000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 7186 Accepted Submission(s): 2298
For each test case, the first line contain two integer n and m (n, m <= 100000), indicates the number of integers in the sequence and the number of the quaere.
The second line contains n integers, describe the sequence.
Each of following m lines contains three integers s, t, k.
[s, t] indicates the interval and k indicates the kth big number in interval [s, t]
題意:
T組數據, 每組數據第一行有兩個數 n,m。接下來一行有 n 個數, 接下來 m 行每行有3個數 l, r, k ,表示求區間[l,r] 內第 k 小的數。
思路:
可以做爲劃分樹的入門題:
建樹過程:
先將原數組 a[] (數組下標從 1 開始)的副本 sort_a[] 升序排序,以第 0 層爲例:
mid = left + (right - left) / 2;然後按快速排序的思路對數組按原順序進行劃分,即比 sort_a[mid] 小的數放到下一層的左邊;比 sort_a[mid] 大的數放到下一層的右邊;與 sort_a[mid] 相等的數則根據下一層左邊缺多少個數來進行填補。比如第 0 層 sort_a[mid] = 4;1,3,2 比 4 小, 放到下一層左邊,而下一層左邊長度爲 mid - left + 1 = 4,所以要把一個 4 補到下一層左邊;剩下的都放到右邊。 下面各層遞歸處理。
然而並沒有這麼簡單,每一層還要維護一個 num[] 域,num[i] 表示區間 [left, i] 中被分配到下一層左邊的數有多少個。比如對於第 0 層,num[5] = 2,因爲區間 [1, 5] 中被分配到第 1 層左邊的數有 2 個(1 和 3)
查找過程:
劃分樹一般爲這樣的結構體:
typedef struct{
type a[N];
type num[N];
}Layer;
Layer lay[M];// M = 20 層可以滿足 n = 2^20 的要求。
由此看出劃分樹的空間複雜度爲 M * N,N 很大時可能相當不理想。
比如這個例子, 對於 ( l = 3,r = 7,k = 2 ) 這個請求,第 0 層的查詢過程爲:
首先計算
sum = lay[0].a[r] - lay[0].a[l-1] = 2 >= k = 2
這裏要注意如果 left == l,則 sum = lay[0].a[r];
所以下一層的查詢應該在左邊進行,可是下一層的查詢區間怎樣確定呢?
可以知道,在不斷往下的時候區間 [l,r] 是不斷縮小的(下圖紅色部分)。第 1 層左邊的藍色的 1 是縮小的部分,右邊的藍色的 2 也是縮小的部分;仔細分析可以發現第 1 層左邊縮小的個數是第 0 層的 num[l-1];第一層右邊縮小的個數是第 0 層的 num[right] - num[r-1]。
左子樹的起始下標爲 left,右子樹的起始下標爲 mid + 1。
因此如果 sum >= k 說明 [l,r] 區間的第 k 小的數在第一層的左邊,進入第一層的左子樹;而區間 [l, r] 應改爲 [ left + num[l-1],left + num[r] - 1 ];
而 sum < k 則進入第一層的右子樹;而區間 [l, r] 應該爲 [ mid + 1 + l - left - num[l-1],mid + 1 + r - left - sum[r] ]
劃分樹的基礎部分大概就是這樣,此題代碼把輸入改改可以解決 POJ 2104 和 POJ 2761。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 100010
int sort_a[N];
typedef struct{
int a[N];
int num[N];
}Layer;
Layer lay[25];
int cmp(void const *_a, void const *_b)
{
int *a = (int *)_a;
int *b = (int *)_b;
return *a - *b;
}
void build(int left, int right, int cur)
{
int i;
int mid;
int lson_p;
int rson_p;
int put_left;
if(left == right)
return;
mid = left + (right - left) / 2;
put_left = mid - left + 1;
for(i = left; i <= right; i++)
if(lay[cur].a[i] < sort_a[mid])
put_left--;
lson_p = left;
rson_p = mid + 1;
for(i = left; i <= right; i++){
if(i == left)
lay[cur].num[i] = 0;
else
lay[cur].num[i] = lay[cur].num[i-1];
if(lay[cur].a[i] < sort_a[mid]){
lay[cur].num[i]++;
lay[cur+1].a[lson_p++] = lay[cur].a[i];
}
else if(lay[cur].a[i] == sort_a[mid]){
if(put_left){
put_left--;
lay[cur].num[i]++;
lay[cur+1].a[lson_p++] = lay[cur].a[i];
}
else
lay[cur+1].a[rson_p++] = lay[cur].a[i];
}
else
lay[cur+1].a[rson_p++] = lay[cur].a[i];
}
build(left, mid, cur + 1);
build(mid + 1, right, cur + 1);
}
int Query(int left, int right, int cur, int l, int r, int k)
{
int mid;
int s;
int ss;
if(left == right)
return lay[cur].a[left];
mid = left + (right - left) / 2;
if(left == l){
s = 0;
ss = lay[cur].num[r];
}
else{
s = lay[cur].num[l-1];
ss = lay[cur].num[r] - s;
}
if(ss >= k)
return Query(left, mid, cur + 1, left + s, left + ss + s - 1, k);
else
return Query(mid + 1, right, cur + 1, mid + 1 + l - left - s, mid + 1 + r - left - ss - s, k - ss);
}
int main()
{
#if 0
freopen("in.txt","r",stdin);
#endif
int T;
scanf("%d", &T);
while(T--){
int n, m;
scanf("%d%d", &n, &m);
int i, j;
for(i = 0; i < n; i++){
scanf("%d", sort_a + i);
lay[0].a[i] = sort_a[i];
}
qsort(sort_a, n, sizeof(int), cmp);
build(0, n - 1, 0);
for(i = 0; i < m; i++){
int l, r, k;
scanf("%d%d%d", &l, &r, &k);
printf("%d\n", Query(0, n - 1, 0, l - 1, r - 1, k));
}
}
return 0;
}