年前就开始写的一个题。。。一直没调。。
中间贡献无数wa原因是忘了函数式的数据结构要开30倍空间。。。。
至今对主席树的理解还是不算很深
主席树大概就是对每个前缀或者后缀都单独的维护一颗线段树,然后由于主席树的可加减性,可以减出来区间的情况
用丽杰姐姐的话来说就是划分树已经成为时代的眼泪。。。。
后来稍微懂了。。。
大概就是首先闹一个 rank
这样我们就知道每个数在现在区间的排名了,相当于是一个离散的过程,当然有重复就麻烦了
然后我们每个节点保存的 就是一个数字而已,大概意思是这段范围内一共出现了几次
但是我们并不保留具体的值
因为主席树是可以减的,所以一直减,一直到叶节点,就显而易见了。。。
这是主席树的精髓。。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#define MAX 100010
using namespace std;
int t[MAX*30];
int n,c[MAX],a[MAX],child[MAX*30][2],size,tree[MAX*30];
int tot=0,m;
bool cmp(int a1,int a2)
{
return a1<a2;
}
void before()
{
for(int i=1;i<=n;i++)
c[i]=a[i];
sort(c+1,c+1+n,cmp);
size=unique(c+1,c+1+n)-(c+1);
}
int build(int la,int ra)
{
int root=tot++;
tree[root]=0;
if(la!=ra)
{
int mid=(la+ra)/2;
child[root][0]=build(la,mid);
child[root][1]=build(mid+1,ra);
}
return root;
}
int find(int x)
{
return lower_bound(c+1,c+1+size,x)-c;
}
int update(int root,int pos,int value)
{
int newnode=tot++,tmp=newnode;
tree[newnode]=tree[root]+value;
int l=1,r=size;
while(l<r)
{
int mid=(l+r)>>1;
if(pos<=mid)
{
child[newnode][0]=tot++;
child[newnode][1]=child[root][1];
newnode=child[newnode][0];
root=child[root][0];
r=mid;
}
else
{
child[newnode][1]=tot++;
child[newnode][0]=child[root][0];
newnode=child[newnode][1];
root=child[root][1];
l=mid+1;
}
tree[newnode]=tree[root]+value;
}
return tmp;
}
int ask(int l,int r,int k)
{
int L=1,R=size;
while(L<R)
{
int mid=(L+R)>>1;
if(tree[child[l][0]]-tree[child[r][0]]>=k)
{
R=mid;
l=child[l][0];
r=child[r][0];
}
else
{
L=mid+1;
k-=tree[child[l][0]]-tree[child[r][0]];
l=child[l][1];
r=child[r][1];
}
}
return L;
}
int main()
{
while(scanf("%d%d",&n,&m)==2)
{
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
before();
tot=0;
t[n+1]=build(1,size);
for(int i=n;i>=1;i--)
{
int pos=find(a[i]);
t[i]=update(t[i+1],pos,1);
}
while(m--)
{
int l,r,k;
scanf("%d %d %d",&l,&r,&k);
printf("%d\n",c[ask(t[l],t[r+1],k)]);
}
}
return 0;
}