POJ 2104 歸併樹 OR 劃分樹
阿新 • • 發佈:2019-02-19
以前做的線段樹都是存了一個值,而現在這顆樹存的是一個數組。儲存從[ l , r )這個左閉右開區間的有序值。
建樹的方法就是歸併排序的過程,因此叫做歸併樹。
如何查詢某區間內的第K大值呢?
用到的一個技巧是2分答案。
我覺得2分是個很好用又很有技巧性的東西。首先是二分割槽間的選擇,這裡選擇是右閉左開,因為當符合條件的優先更新右邊。
對於第K大的數,不大於第K大的數的數量一定要大於等於K,所以對於這個數X,x越大越能滿足這個條件,所以在滿足條件的情況下不斷的減小這個X的值,也就是更新右區間。
另外,STL有merge函式,還有個inplace_merge,還可以自己寫compare函式。
#include<stdio.h> #include<vector> #include<algorithm> using namespace std; vector<int> dat[500000]; // STL 都是使用的左閉右開區間,自己的線段樹最好也使用這樣的區間 int A[100003]; int L[100003]; int R[100003]; int K[100003]; int nums[100003]; int build(int k,int l,int r) { if(r-l==1) { dat[k].push_back(A[l]); } else { int mid=(l+r)>>1; int ls=k*2; int rs=k*2+1; build(ls,l,mid); build(rs,mid,r); dat[k].resize(r-l); merge(dat[ls].begin(),dat[ls].end(),dat[rs].begin(),dat[rs].end(),dat[k].begin()); } } //不大於x的有多少個 int query(int a,int b,int k,int l,int r,int x) { if(b<=l||a>=r) return 0; if(a<=l&&r<=b) return upper_bound(dat[k].begin(),dat[k].end(),x)-dat[k].begin(); int mid=(l+r)>>1; int v1=query(a,b,k*2,l,mid,x); int v2=query(a,b,k*2+1,mid,r,x); return v1+v2; } int main() { int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%d",&A[i]); nums[i]=A[i]; } for(int i=1;i<=m;i++) scanf("%d%d%d",&L[i],&R[i],&K[i]); sort(nums+1,nums+n+1); build(1,1,n+1); for(int i=1;i<=m;i++) { int ub=n; int lb=0; int k=K[i]; while(ub-lb>1) { int mid=(ub+lb)>>1; int x=nums[mid]; int c=query(L[i],R[i]+1,1,1,n+1,x); if(c>=k) ub=mid; else lb=mid; } printf("%d\n",nums[ub]); } scanf("%d",&n); return 0; }