主席树模板,求静态区间第k小。
据说主席树又叫可持久化线段树,因此它肯定跟线段树很有关系。
我们用n棵线段树来维护,第i棵线段树维护的是前1~i个元素的值。而每棵线段树上同一位置的节点维护的范围是一样的,若第i个元素的值是x,我们就在线段树里把x这个位置与管理它的节点的值都加1。
因为每一棵线段树的结构都是一样的,所以我们可以合并它们。
我们每次只需根据新输入的第i个值建一条链,然后与第i-1棵线段树合并(在当前链上累加,其他直接取等)。
而我们在求l ~ r范围内的第k小时,只需把第r棵线段树-第l-1棵线段树,就可以得到l ~ r范围的信息了。而我们在寻找时只需不停地找当前节点范围内的左儿子的值(小于mid的元素数量)c,若k<=c,去左儿子里找,反之去右儿子里找。
Code:
#include<cstdio>
#include<cstdlib>
#define mid (l+r)/2
#define INF 1e9+1
int d[210000],lc[20100000],rc[20100000],tot[20100000],rt[20100000];
int n,m,v,len(0);
void update(int x,int &now,int l,int r)
{
if(!now) now=++len;
tot[now]=tot[x]+1;
if(l==r) return;
if(v<=mid)
{
rc[now]=rc[x];
update(lc[x],lc[now],l,mid);
}
else
{
lc[now]=lc[x];
update(rc[x],rc[now],mid+1,r);
}
}
int find_kth(int x,int y,int k)
{
int xx=rt[x-1],yy=rt[y];
int l=-INF,r=INF;
while(l<r)
{
int c=tot[lc[yy]]-tot[lc[xx]];
if(k<=c)
{
xx=lc[xx];yy=lc[yy];
r=mid;
}
else
{
xx=rc[xx];yy=rc[yy];
l=mid+1;k-=c;
}
}
return l;
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&d[i]);
for(int i=1;i<=n;i++)
{
v=d[i];
update(rt[i-1],rt[i],-INF,INF);
}
for(int i=1;i<=m;i++)
{
int l,r,k;
scanf("%d %d %d",&l,&r,&k);
printf("%d\n",find_kth(l,r,k));
}
}