Chlience的博客

其实,我们都曾彷徨

[题解] POJ 2104 K-th Number

POJ 2104 K-th Number

题目描述 Description
要求你维护一个数据结构,能够返回区间[l,r]之间第k小的值

输入描述 Input Description
第一行两个数n,m,分别表示数组大小和询问组数
第二行n个不同的整数a1..an,表示数组的第i位是
接下来m行每行三个正整数l,r,k,表示询问[l,r]区间中第k小的值

输出描述 Output Description
共m行,每行一个正整数表示询问的答案

样例输入 Sample Input
7 3
1 5 2 6 3 7 4
2 5 3
4 4 1
1 7 3

样例输出 Sample Output
5
6
3

数据范围及提示 Data Size & Hint
1<=n<=100000,1<=m<=5000,ai<=109

Solution

这是一道可持久化线段树(主席树)的模板题
主席树的本质是保存几个形态完全相同的历史版本
那么每加入数组的一位都建出一个船新的版本
这样的话第i个版本就保存了前i个数的所有信息
所以说我们需要查询[l,r]的信息只需要将第r个版本-第l-1个版本就好了

那么每棵线段树维护什么信息呢?
因为要求区间第k小,并且已经能将[l,r]中的点信息提取出来,考虑按大小排序后搞个前缀和查询即可
那么线段树的叶子节点保存这个叶子节点所对应的数的出现次数,非叶子节点保存左右节点和即可

每次查询当前节点的左儿子大小和k的大小关系即可

详细代码如下:

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
const int N=200010;
const int INF=0x3f3f3f3f;
int num[N],sorted[N],root[N];
int cnt;
struct T {
    int sum,l,r;
    T(){}
    T(int _sum,int _l,int _r){sum=_sum;l=_l;r=_r;};
}t[N<<5];
void insert(int &root,int pos,int l,int r) {
    t[++cnt]=T(t[root].sum+1,t[root].l,t[root].r);
    root=cnt;
    if(l==r) return;
    int m=(l+r)>>1;
    if(pos<=m)
        insert(t[root].l,pos,l,m);
    else
        insert(t[root].r,pos,m+1,r);
}
int query(int S,int E,int l,int r,int k) {
    if(l==r) return l;
    int m=(l+r)>>1;
    int sum=t[t[E].l].sum-t[t[S].l].sum;
    if(k<=sum)
        return query(t[S].l,t[E].l,l,m,k);
    else
        return query(t[S].r,t[E].r,m+1,r,k-sum);
}
int main() {
    int n,m,NUM,pos,T;
    while(~scanf("%d %d",&n,&m)) {
        cnt=0;root[0]=0;
        for(int i=1;i<=n;i++) {
            scanf("%d",&num[i]);
            sorted[i]=num[i];
        }
        sort(sorted+1,sorted+n+1);
        NUM=unique(sorted+1,sorted+1+n)-(sorted+1);
        for(int i=1;i<=n;i++) {
            root[i]=root[i-1];
            pos=lower_bound(sorted+1,sorted+NUM+1,num[i])-sorted;
            insert(root[i],pos,1,NUM);
        }
        int l,r,k;
        while(m--) {
            scanf("%d%d%d",&l,&r,&k);
            pos=query(root[l-1],root[r],1,NUM,k);
            printf("%d\n",sorted[pos]);
        }
    }
    return 0;
}
阅读更多
文章标签: 主席树
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

不良信息举报

[题解] POJ 2104 K-th Number

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭