【9018:2208】可持久化线段树2

2208: 【模板】可持久化线段树2

时间限制: 3 Sec 内存限制: 256 MB
提交: 30 解决: 12
[提交][状态][讨论版]

题目描述

静态区间第K小问题是典型的主席树模板。

在这个问题中,你需要实现对区间第K小的查询。

输入

第1行,输入两个正整数n,m,表示数列长度,查询次数。

第2行,n个整数表示数列ai。

接下来m行,每行3个正整数l,r,k表示查询[l,r]内的第k小数。(1<=l<=r<=n,1<=k<=r-l+1)

输出

输出m行,每行1个整数表示答案。

样例输入

5 5
25957 6405 15770 26287 26465 
2 2 1
3 4 1
4 5 1
1 2 2
4 4 1

样例输出

6405
15770
26287
25957
26287

提示

对于100%的数据满足:1≤N,M≤5⋅1051 leq N, M leq 2cdot 10^5

对于数列中的所有数aia_i,均满足−109≤ai≤109-{10}^9 leq a_i leq {10}^9

代码如下:

#include<cstdio>
#include<iostream>
#include<algorithm>
#define MN 500005
#define MM 10000005
using namespace std;
int n,q,v[MN],rk[MN],vv[MN],cnt,T[MM],ls[MM],rs[MM],rt[MM];
bool cmp(int x,int y){return v[x]<v[y];}
void update(int &k,int l,int r,int v){
    ls[++cnt]=ls[k],rs[cnt]=rs[k],k=cnt;
    if(l==r){T[k]=1;return;}
    int mid=(l+r)>>1;
    if(v<=mid) update(ls[k],l,mid,v);
    else update(rs[k],mid+1,r,v);
    T[k]=T[ls[k]]+T[rs[k]];
}
int query(int k,int l,int r,int ql,int qr){
    if(l==r) return l;
    int mid=(l+r)>>1;
    if(k<=T[ls[qr]]-T[ls[ql]]) return query(k,l,mid,ls[ql],ls[qr]);
    else return query(k-=T[ls[qr]]-T[ls[ql]],mid+1,r,rs[ql],rs[qr]);
}
int main()
{
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++) scanf("%d",&v[i]),rk[i]=i;
    sort(rk+1,rk+1+n,cmp);
    for(int i=1;i<=n;i++) vv[rk[i]]=i;
    //for(int i=1;i<=n;i++) printf("%d
",rk[i]);
    for(int i=1;i<=n;i++) rt[i]=rt[i-1],update(rt[i],1,n,vv[i]);
    for(int i=1;i<=q;i++){
        int x,y,k; scanf("%d%d%d",&x,&y,&k);
        printf("%d
",v[rk[query(k,1,n,rt[x-1],rt[y])]]);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/Beginner-/p/8529053.html