1. 程式人生 > >POJ 2104 K-th Number ( 求取區間 K 大值 )

POJ 2104 K-th Number ( 求取區間 K 大值 )

二分法 esp size 麻煩 == 平方分割 closed push_back ret

題意 : 給出一個含有 N 個數的序列,然後有 M 次問詢,每次問詢包含 ( L, R, K ) 要求你給出 L 到 R 這個區間的第 K 大是幾

分析 :

求取區間 K 大值是個經典的問題,可以使用的方法有很多,我聽過的只有主席樹、整體二分法、劃分樹、分塊……

因為是看《挑戰》書介紹的平方分割方法(分塊),所以先把分塊說了,其他的坑以後再填

分塊算法思想是將區間分為若幹塊,一般分為 n1/2 塊然後在每塊維護所需信息,可以把復雜度降到 O(根號n)

具體的分析和代碼在《挑戰程序設計競賽》有很詳細的解釋,這裏說一下代碼的實現細節

題目在實現的時候用的是這種 [L, R) 左閉右開區間,這樣的區間表示法在 STL 和 JAVA的類庫中很常用

這樣有很多優點,其中一個優點就是區間的長度是L ~ R,而判斷兩個區間的交或者並的時候思考的難度也降低很多。

L < R代表區間有值,L == R代表區間到了最後。用閉區間就特別麻煩,下面我給出的代碼就是用閉區間的,糾結了我好久...

技術分享圖片
#include<vector>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int B = 1000;
const int maxn = 1e5 + 10;
vector<int> bucket[maxn / B];
int num[maxn], arr[maxn];
int N, M; int main(void) { while(~scanf("%d %d", &N, &M)){ for(int i=0; i<N; i++){ scanf("%d", &arr[i]); bucket[i / B].push_back(arr[i]); num[i] = arr[i]; } sort(num, num + N); for(int i=0; i<N/B; i++) sort(bucket[i].begin(), bucket[i].end());
int L, R, K; while(M--){ scanf("%d %d %d", &L, &R, &K); L--, R--; int lb = 0, ub = N - 1, ans = -1; while(ub >= lb){ int mid = lb + ((ub - lb)>>1); int c = 0; int TL = L, TR = R; while(TR+1 > TL && TL % B != 0) if(arr[TL++] <= num[mid]) c++; while(TR+1 > TL && (TR+1) % B != 0) if(arr[TR--] <= num[mid]) c++; while(TR >= TL){ c += upper_bound(bucket[TL/B].begin(), bucket[TL/B].end(), num[mid]) - bucket[TL/B].begin(); TL += B; } if(c >= K) ans = mid, ub = mid - 1; else lb = mid + 1; } printf("%d\n", num[ans]); } } return 0; }
View Code

POJ 2104 K-th Number ( 求取區間 K 大值 )