Optimal Subsequences(主席树)

题意:

给定一个序列,长度为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
",a[p]);
    }
}
原文地址:https://www.cnblogs.com/ucprer/p/11950065.html