前言:
最近心(po)血(yu)来(ya)潮(li)学习了一下主席树。
主席树,即可持久化线段树,支持维护和查询区间的第\(k\)大、区间不同种类个数等,基于线段树的思想之上
结构分析:
主席树会维护每次加入时通过点的个数(可以理解为,一颗弹珠从树根)
下面来张动图(PowerPoint冠名)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define f(x,y) for(register int i=x;i<=y;i++)
#define G ch=getchar()
#define rd int
#define in(x) x=read();
#define node int
#define mid ((l+r)>>1)
#define fm(x) memset(x,0,sizeof(x))
#define maxN 200000
using namespace std;
inline rd read();
class president_tree
{
public:
node query(int x,int y,int w) {return rank[_query(root[x-1],root[y],1,_size,w)];}
void build(int a[],int n)
{
tot=0; fm(rank); fm(root); fm(sum); fm(L); fm(R);
for(register int i=1;i<=n;i++) rank[i]=a[i];
sort(rank+1,rank+n+1);
_size=unique(rank+1,rank+n+1)-rank-1;
root[0]=_build(1,_size);
for(register int i=1;i<=n;i++)
root[i]=update(root[i-1],1,_size,lower_bound(rank+1,rank+_size+1,a[i])-rank);
}
node update(int pre,int l,int r,int w)
{
int k=++tot; L[k]=L[pre]; R[k]=R[pre]; sum[k]=sum[pre]+1;
if(l<r)
if(w<=mid) L[k]=update(L[pre],l,mid,w);
else R[k]=update(R[pre],mid+1,r,w);
return k;
}
node _query(int u,int v,int l,int r,int k)
{
if(l>=r) return l;
int w=sum[L[v]]-sum[L[u]];
if(w>=k) return _query(L[u],L[v],l,mid,k);
else return _query(R[u],R[v],mid+1,r,k-w);
}
private:
int tot,_size;
node rank[maxN+1],root[maxN+1],sum[(maxN<<5)+1],L[(maxN<<5)+1],R[(maxN<<5)+1];;
node _build(int l,int r)
{
int k=++tot; sum[k]=0;
if(l<r)
{
L[k]=_build(l,mid);
R[k]=_build(mid+1,r);
}
return k;
}
};
int n,m,a[maxN+1],b[maxN+1],x,y,w;
president_tree tree;
int main()
{
in(n); in(m); f(1,n) in(a[i]);
tree.build(a,n);
f(1,m)
{
in(x); in(y); in(w);
printf("%d\n",tree.query(x,y,w));
}
return 0;
}
inline rd read()
{
char ch=getchar();
rd num=0,f=1;
while((ch<'0' || ch>'9') && ch!='-') G;
if(ch=='-') {f=-1; G;}
while(ch>='0' && ch<='9') {num=num*10+ch-'0'; G;}
return num*f;
}