主席树的模板。
主席树用 n 棵线段树记录 [ 1 , i ] 段数字在区间内出现的个数。
如果数的范围很大,那就先离散化。
首先,每棵线段树有三个元素:左孩子编号,右孩子编号,区间内元素的个数。
设它的元素值为 [ l , r ],mid = ( l + r ) / 2,它的左孩子就记录了 [ l , mid ],右孩子就记录了 [ mid + 1 , r ]。
插入一个元素,如果它大于 mid,那么它就存在于当前线段树的右孩子,否则就插入到左孩子中,直到 l = r。
查找区间第 k 大,就只需要判断它的左孩子中的元素个数是否不大于 k,类似平衡树。
#include"cstdio" #include"cctype" #include"algorithm" using namespace std; const int maxn=100001,maxo=2000001; int read() { int c,x=0,s=1; while(!isdigit(c=getchar())) if(c==‘-‘) s=-1; while(x=x*10+c-‘0‘,isdigit(c=getchar())); return x*s; } struct number { int v,id; }b[maxn]; int comp(number a,number b) { return a.v<b.v; } int cnt,rot,a[maxn],c[maxo][2],s[maxo],p[maxo],map[maxo]; void insert(int &o,int e,int l,int r,int v) { if(o==0) o=++cnt; if(l==r) { s[o]=1; return; } s[o]=s[e]+1; int mid=l+r>>1; if(v>mid) { c[o][0]=c[e][0]; insert(c[o][1],c[e][1],mid+1,r,v); } else { c[o][1]=c[e][1]; insert(c[o][0],c[e][0],l,mid,v); } } int query(int o,int e,int l,int r,int k) { if(l==r) return l; int t=s[c[o][0]]-s[c[e][0]],mid=l+r>>1; if(k<=t) return query(c[o][0],c[e][0],l,mid,k); else return query(c[o][1],c[e][1],mid+1,r,k-t); } int main() { int n=read(),m=read(),w=1; for(int i=1;i<=n;i++) a[i]=b[i].v=read(),b[i].id=i; sort(b+1,b+n+1,comp); a[b[1].id]=1; map[1]=b[1].v; for(int i=2;i<=n;i++) if(b[i-1].v==b[i].v) a[b[i].id]=w; else { a[b[i].id]=++w; map[w]=b[i].v; } for(int i=1;i<=n;i++) insert(p[i],p[i-1],1,w,a[i]); while(m--) { int l=read(),r=read(),k=read(); printf("%d\n",map[query(p[r],p[l-1],1,w,k)]); } return 0; }