题意:给你n个不同的数字,m次询问,每次询问l->r这个区间内第k大的树是多少
分析:刚学主席树试着写了一发,值得注意的是主席树是多颗线段树,并且每颗线段树存的都是插入第i个数字的状态,
树中结点存储的是每个区间插入第i个数字之后总共插入了多少个数字,每一次的插入过程都是logn的建树。
根据主席树的这个特点,只需要找到插入l-1这个数字时的线段树状态和r时的状态,根据两者区间插入数字的差值
就能从中找到第k大。
#include<cstring> #include<string> #include<iostream> #include<queue> #include<cstdio> #include<algorithm> #include<map> #include<cstdlib> #include<cmath> #include<vector> //#pragma comment(linker, "/STACK:1024000000,1024000000"); using namespace std; #define INF 0x3f3f3f3f #define maxn 100005 vector<int>v; int cnt; int root[maxn]; int a[maxn]; struct node { int l,r; int siz; }T[maxn*40]; int getid(int x) { return (lower_bound(v.begin(),v.end(),x)-v.begin())+1; } void init() { for(int i=0;i<maxn;i++) root[i]=0; for(int i=0;i<40*maxn;i++) T[i].l=T[i].r=T[i].siz=0; cnt=0; } void update(int pos,int l,int r,int &x,int y) { T[++cnt]=T[y],T[cnt].siz++,x=cnt;//找到需要更新的结点,申请新的结点替换掉 if(l==r) { return ; } int mid=(l+r)>>1; if(pos<=mid) update(pos,l,mid,T[x].l,T[y].l); else update(pos,mid+1,r,T[x].r,T[y].r); } int query(int l,int r,int x,int y,int k) { if(l==r) return l; int mid=(l+r)>>1; int siz=T[T[y].l].siz-T[T[x].l].siz; if(k<=siz) return query(l,mid,T[x].l,T[y].l,k); else return query(mid+1,r,T[x].r,T[y].r,k-siz); } int main() { int n,m; while(scanf("%d%d",&n,&m)!=EOF) { v.clear(); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); v.push_back(a[i]); } init(); sort(v.begin(),v.end()); for(int i=1;i<=n;i++) { int p=getid(a[i]); update(p,1,n,root[i],root[i-1]); } while(m--) { int l,r,k; scanf("%d%d%d",&l,&r,&k); printf("%d\n",v[query(1,n,root[l-1],root[r],k)-1]); } } return 0; }