描述
cjBBteam拥有一个很大的野生动物园。这个动物园坐落在一个狭长的山谷内,这个区域从南到北被划分成N个区域,每个区域都饲养着一头狮子。这些狮子从北到南编号为1,2,3,…,N。每头狮子都有一个觅食能力值Ai,Ai越小觅食能力越强。饲养员cmdButtons决定对狮子进行M次投喂,每次投喂都选择一个区间[I,J],从中选取觅食能力值第K强的狮子进行投喂。值得注意的是,cmdButtons不愿意对某些区域进行过多的投喂,他认为这样有悖公平。因此cmdButtons的投喂区间是互不包含的。你的任务就是算出每次投喂后,食物被哪头狮子吃掉了。
格式
输入格式
输入第一行有两个数N和M。此后一行有N个数,从南到北描述狮子的觅食能力值。此后M行,每行描述一次投喂。第t+2的三个数I,J,K表示在第t次投喂中,cmdButtons选择了区间[I,J]内觅食能力值第K强的狮子进行投喂。
输出格式
输出有M行,每行一个整数。第i行的整数表示在第i次投喂中吃到食物的狮子的觅食能力值。
样例1
样例输入1
7 2
1 5 2 6 3 7 4
1 5 3
2 7 1
样例输出1
3
2
限制
各个测试点2s
提示
对于100%的数据,有1<=N<=100000,1<=M<=50000。
来源
周戈林
解题方法:可持久化线段树求区间第k小
#include <cstdio>
#include <algorithm>
using namespace std;
int n,m,nn,l,r,k;
int a[100009],list[100009];
struct str
{
int tot,x;
int cnt[1800009],son[1800009][2],root[100009];
int insert(int &k,int p,int l,int r,int v)
{
tot++;
k=tot;
cnt[k]=cnt[p]+1;
if (l==r)
{
return 0;
}
int m=(l+r)>>1;
if (v<=m)
{
son[k][1]=son[p][1];
insert(son[k][0],son[p][0],l,m,v);
}
else
{
son[k][0]=son[p][0];
insert(son[k][1],son[p][1],m+1,r,v);
}
return 0;
}
int insert(int id,int v)
{
return insert(root[id],root[id-1],1,nn,v);
}
int search(int x,int y,int k)
{
x=root[x-1];
y=root[y];
int l=1,r=nn,m;
while (l<r)
{
m=(l+r)>>1;
if (cnt[son[y][0]]-cnt[son[x][0]]>=k)
{
r=m;
x=son[x][0];
y=son[y][0];
}
else
{
k-=cnt[son[y][0]]-cnt[son[x][0]];
l=m+1;
x=son[x][1];
y=son[y][1];
}
}
return list[l];
}
};
str tree;
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
list[i]=a[i];
}
sort(list+1,list+n+1);
nn=unique(list+1,list+n+1)-list-1;
for (int i=1;i<=n;i++)
{
a[i]=lower_bound(list+1,list+nn+1,a[i])-list;
}
for (int i=1;i<=n;i++)
{
tree.insert(i,a[i]);
}
for (int i=1;i<=m;i++)
{
scanf("%d%d%d",&l,&r,&k);
printf("%d\n",tree.search(l,r,k));
}
return 0;
}