POJ2104 K-th Number (平方分割 + 二分)
阿新 • • 發佈:2021-01-20
題目連結:傳送門
題意:輸入n個數,然後進行m次操作,每次操作輸入三個數l,r,k,輸出在[l,r]區間第k小的數
解題思路:這道題做法倒是挺多的,平方分割可以做,歸併樹,劃分樹,主席樹都能做,但是本片部落格主要講解一下平方分割的做法(比較簡單),我們把n個數分為n/1000個桶,然後我們維護每個桶即可,我們通過預處理把每個桶的元素進行排序處理,然後我們二分查詢第k小的數字,在[l,r]這個區間比第第k小的數字肯定只有k-1個,比第k大的數字只有(r-l+1)-k個,我們會發現我們查詢的區間無非就兩種情況,第一種,就是查詢區間完全被包含在區間內,第二種就是所在的桶不完全包含區間內的元素,需要我們逐步處理,時間複雜度為\(O(nlogn+m\sqrt{n}log^1.5n)\)
Code:
#include<cstdio> #include<algorithm> #include<cstring> #include<vector> using namespace std; const int B = 1000, N = 100005; int n,m; int a[N],b[N]; vector<int> ton[N/B+1]; int main() { int t; scanf("%d%d",&n,&m); for(int i = 0;i < n; ++i) { scanf("%d",&a[i]); ton[i/B].push_back(a[i]); b[i] = a[i]; } sort(b,b+n); for(int i = 0,len = n/B;i < len; ++i) { sort(ton[i].begin(),ton[i].end());//對每一個桶進行預處理 } int l,r,k; while(m--) { scanf("%d%d%d",&l,&r,&k); l--; int lb = -1, rb = n; while(lb + 1 < rb) {//二分查詢x int mid = (lb + rb) >> 1; int x = b[mid]; int tl = l,tr = r, c = 0; while(tl < tr && tl % B != 0) if(a[tl++] <= x) c++;//左邊不在同一個桶裡面的 while(tl < tr && tr % B != 0) if(a[--tr] <= x) c++;//右邊不在同一個桶裡面的 while(tl < tr) { int bb = tl /B; c += upper_bound(ton[bb].begin(),ton[bb].end(),x) - ton[bb].begin(); tl += B; } if(c >= k) rb = mid; else lb = mid; } printf("%d\n",b[rb]); } return 0; }
PS:同樣的題目POJ能AC,在HDU會T,原因因該是HDU的資料和POJ的不太同,但是HDU的可以通過其他三種方法做出,學後再出關於歸併樹,劃分樹,主席樹的做法