题意:
给定一个序列,长度为n(<=2e5),有m(<=2e5)个询问,每个询问包含k和pos,要从原序列中选出一个长度为k的子序列,要求是这个序列的和是所有长度为k的序列中最大的,且是字典序最小的,要求输出子序列中第pos个元素。
思路:
显然要是长度为k的序列中和最大,必须要优先选大的数。显然必须全部选的数不需要考虑位置,部分选的数(必定是最小的数)肯定优先选择坐标小的,因此可以将所有数按数值从大到小排,然后坐标从小到达排,前k的数就是每次要选的数。而第pos个元素就是排好的数前k个中坐标值第pos小的,用主席树维护坐标值就好了。
#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
const int Log=40;
int num[maxn*Log],lson[maxn*Log],rson[maxn*Log];
int root[maxn];
int tot;
int build(int l,int r){
int pos=++tot;
if(l<r){
int mid=(l+r)>>1;
lson[pos]=build(l,mid);
rson[pos]=build(mid+1,r);
}
return pos;
}
int update(int rt,int l,int r,int p){
int pos=++tot;
num[pos]=num[rt]+1;
lson[pos]=lson[rt];
rson[pos]=rson[rt];
if(l<r){
int mid=(l+r)>>1;
if(p<=mid) lson[pos]=update(lson[rt],l,mid,p);
else rson[pos]=update(rson[rt],mid+1,r,p);
}
return pos;
}
int query(int Old,int New,int l,int r,int k){
if(l==r)return l;
int mid=(l+r)>>1;
int x=num[lson[New]]-num[lson[Old]];
if(x>=k) return query(lson[Old],lson[New],l,mid,k);
else return query(rson[Old],rson[New],mid+1,r,k-x);
}
struct node{
int v,pos;
}N[maxn];
bool cmp(node a,node b){
return a.v==b.v?a.pos<b.pos:a.v>b.v;
}
int a[maxn];
int main(){
int n,q;
cin>>n;
for(int i=1;i<=n;i++){
scanf("%d",&N[i].v);
N[i].pos=i;
a[i]=N[i].v;
}
sort(N+1,N+1+n,cmp);
for(int i=1;i<=n;i++)
root[i]=update(root[i-1],1,n,N[i].pos);
root[0] = build(1, n);
cin>>q;
while(q--){
int k,pos;scanf("%d%d",&k,&pos);
int p=query(root[0],root[k],1,n,pos);
printf("%d\n",a[p]);
}
}