题意:给你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;
}