看过一些论文,之后YY了好久……十一几天玩的实在无聊了突然想起来就把这题做了,居然是1A真是感动……
/*
有一种线段树搞全局k大值的办法:
先排序离散化之后维护区间数字出现次数,如果左子树数字总数多于k就在左子树找,不然在右子树找排名第(k-左子树和)的数……
改成区间的话,有一种办法就是每个点来一棵树,第x棵树表示从1到x维护上面所说的线段树
类似前缀和,这样两棵树做差就是目标区间维护每个数字出现次数的线段树
考虑x到x+1棵树只需要变一个叶子的值,所以只要进行一次修改
而一次修改只改了树从根到叶子的一条路径所以这两棵树大部分点可以共用,可以避免空间浪费
具体做法,每次修改的时候先复制一下当前点,递归修改要修改的节点后返回新点……这应该就是可持久化了吧……
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef int SegT;
struct Node{int l,r,cnt;} node[5000000];
int nown=0,segt[100001];
SegT build(int l,int r) {
int t=nown++,mid=l+r>>1;
if (l==r) node[t]=(Node){0,0,0};
else node[t]=(Node){build(l,mid),build(mid+1,r),0};
return t;
}
SegT change(SegT t,int l,int r,int p,int k) {
int tmp=nown++,mid=l+r>>1;
if (l==r) {node[tmp]=(Node){0,0,node[t].cnt+k}; return tmp;}
else if (p<=mid) node[tmp]=(Node){change(node[t].l,l,mid,p,k),node[t].r,0};
else node[tmp]=(Node){node[t].l,change(node[t].r,mid+1,r,p,k),0};
node[tmp].cnt=node[node[tmp].l].cnt+node[node[tmp].r].cnt;
return tmp;
}
int select(SegT a,SegT b,int l,int r,int k) {
a=segt[a-1],b=segt[b];
for (int mid=l+r>>1; l<r; mid=l+r>>1) {
if (node[node[b].l].cnt-node[node[a].l].cnt>=k) {a=node[a].l; b=node[b].l; r=mid;}
else {k-=(node[node[b].l].cnt-node[node[a].l].cnt); a=node[a].r; b=node[b].r; l=mid+1;}
}
return l;
}
int a[100001],b[100001],n;
int get(int x) {
int l=1,r=n+1;
for (int mid=l+r>>1; l+1<r; mid=l+r>>1)
if (x<b[mid]) r=mid; else l=mid;
return l;
}
int main() {
int m; scanf("%d%d",&n,&m);
for (int i=1; i<=n; ++i) {scanf("%d",&a[i]); b[i]=a[i];};
sort(&b[1],&b[n+1]);
segt[0]=build(1,n);
for (int i=1; i<=n; ++i) segt[i]=change(segt[i-1],1,n,get(a[i]),1);
//for (int i=1; i<=n; ++i) cout << b[i] << ' ' << get(a[i]) << endl;
//for (int i=0; i<=n; ++i) {check(segt[i],1,n); puts("");}
while (m--) {
int l,r,k;
scanf("%d%d%d",&l,&r,&k);
printf("%d\n",b[select(l,r,1,n,k)]);
}
//system("pause");
}
唉……荒废太久了……