主席树
主席树的本质就是线段树,但是比线段的优势是可以保留历史状态,也就是后面建立的树通过减去前面的树——节点相减产生一颗“新”的线段树然后解决区间第k大的问题。
每次建立线段树其实只是添加了logn个节点然后连到已有的线段树,通过root数组保持最上面的根节点的数组下标
然后就是主席树的每个节点维护的当前区间有多少个数。
#include <stdio.h>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn=1e5+10;
int root[maxn],a[maxn],x,y,r,l,k,cnt;//root数组表示新建的树的根节点的下标
vector<int>v;
struct node
{
int r,l,sum;
}t[maxn*40];
int getid(int x)
{
return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}
void Update(int l,int r,int &pre,int pos)//pre表示前一颗树节点的下标 使用应用进行实时更新
{
int rt=cnt++;
//cout<<"rt "<<rt<<" cnt="<<cnt<<endl;
t[rt]=t[pre],t[rt].sum++,pre=rt;
if(r==l)
return ;
int mid=(r+l)>>1;
if(mid>=pos)
Update(l,mid ,t[pre].l,pos);
else
Update(mid+1,r,t[pre].r,pos);
}
int Queuer(int l,int r,int x,int y,int k)
{
if(l==r)
return r;
int sum=t[t[y].l].sum-t[t[x].l].sum;
int mid=(r+l)>>1;
if(sum>=k)
return Queuer(l,mid,t[x].l,t[y].l,k);
else
return Queuer(mid+1,r,t[x].r,t[y].r,k-sum);
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int m,n;
cin>>n>>m;
for(int i=1; i<=n; i++)
cin>>a[i],v.push_back(a[i]);
sort(v.begin(),v.end()), v.erase(unique( v.begin(),v.end()),v.end());//离散化黑科技
root[0]=0,t[0].r=t[0].l=t[0].sum=0;
cnt=1;//cnt 为0将覆盖root[0]出错
for(int i=1; i<=n; i++)
{
root[i]=root[i-1];
Update(1,n,root[i],getid(a[i]));//所谓的更新其实就是添加节点建树
}
while(m--)
{
cin>>x>>y>>k;
cout<<v[Queuer(1,n,root[x-1],root[y],k)-1]<<endl;
}
return 0;
}