K-th Number
题意:
求出【l,r】区间第k大数。
分析:
学的似懂非懂,我的理解是建立n个线段树,每个线段树保留之前版本的信息,通过节点的复用减少空间复杂度。因为我们发现当我们改变一个节点时,只有一条链上的节点的信息需要更新,所以我们每次新建一个线段树时,其实只要建立那条链上的节点个数的新节点,其他可以通过节点指向以前的节点。
对于求第k大的树,可以每次把数据离散化后(数据就变成了1~n),然后依次对sum数组进行初始化,类似用线段树找逆序数一样,从l点对应的sum值开始累加,一直加到i点时,如果sum大于等于k,那么我们要找到的数就是Hash数组里第i大的数,即Hash【i】。
代码:

#include <map> #include <queue> #include <vector> #include <math.h> #include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> using namespace std; #define ll long long #define ull unsigned long long #define cls(x) memset(x,0,sizeof(x)) #define clslow(x) memset(x,-1,sizeof(x)) #define lson l,m #define rson m+1,r const int maxn=1e5+100; const int inf=0x3f3f3f3f; int n,m,tot; int a[maxn],Hash[maxn]; int L[maxn<<2],R[maxn<<2],T[maxn<<2],sum[maxn<<2]; int build(int l,int r) { int rt=++tot; sum[rt]=0; if(l<r){ int m=(l+r)>>1; L[rt]=build(lson); R[rt]=build(rson); } return rt; } int update(int pre,int l,int r,int x) { int rt=++tot; L[rt]=L[pre]; R[rt]=R[pre]; sum[rt]=sum[pre]+1; if(l<r){ int m=(l+r)>>1; if(x<=m) L[rt]=update(L[pre],lson,x); else R[rt]=update(R[pre],rson,x); } return rt; } int query(int u,int v,int l,int r,int k) { if(l>=r) return l; int m=(l+r)>>1; int num=sum[L[v]]-sum[L[u]]; if(num>=k) return query(L[u],L[v],lson,k); else return query(R[u],R[v],rson,k-num); } int binarySearch(int key,int* a,int n) { int l=1,r=n; while(l<=r) { int mid=(l+r)>>1; if(a[mid]==key) return mid; else if(a[mid]<key) l=mid+1; else if(a[mid]>key) r=mid-1; } return -1; } int main() { // freopen("in.txt","r",stdin); while(scanf("%d%d",&n,&m)!=EOF) { tot=0; for(int i=1;i<=n;i++){ scanf("%d",&a[i]); Hash[i]=a[i]; } sort(Hash+1,Hash+n+1); int d=1; for(int i=2;i<=n;i++){ if(Hash[i]!=Hash[i-1]) Hash[++d]=Hash[i]; } T[0]=build(1,d); for(int i=1;i<=n;i++){ int x=binarySearch(a[i],Hash,d); T[i]=update(T[i-1],1,d,x); } for(int i=1;i<=m;i++){ int l,r,k; scanf("%d%d%d",&l,&r,&k); int p=query(T[l-1],T[r],1,d,k); printf("%d ",Hash[p]); } } return 0; }