POJ

来源:互联网 发布:海岛奇兵 科技升级数据 编辑:程序博客网 时间:2024/05/22 13:04

点我看题

题意:给出一个序列,然后求其某个区间内的第k大值。

分析:主席树的模板题,这题不涉及到修改操作,只有查询。

先来看看主席树是什么。

主席树,也就是可持久化的线段树,又称函数式线段树。(看到知乎上对主席树和可持久化线段树有一些区分

那么,可持久化是什么意思呢,目前认为就是去利用前面已经知道的一些数据来减少当前对空间的消耗,通俗的讲,就是我之前已经知道那些值是什么了,那么我现在还要想知道那些值,就直接指向前面就好,不用特意记下来。

主席树的结构就是n+1棵线段树,第i棵线段树记录前i个数的情况。

那么对于这个题来说,我们也可以用主席树求解。

参考代码:

/*主席树*/#include<cstdio>#include<cmath>#include<cstring>#include<algorithm>#include<iostream>using namespace std;const int maxn = 1e5+10;int n,q,m;int a[maxn],hs[maxn];//a为原序列,hs为对a进行hash后的结果//主席树int tot;//总结点数int pt[maxn<<5],lson[maxn<<5],rson[maxn<<5],c[maxn<<5];//结点值,左右儿子,结点对应区间内存在的数的个数//对原序列做一个哈希,哈希函数为f(x) = x;0//m为哈希后的序列长度void InitHash(){    for( int i = 1; i <= n; i++)        hs[i] = a[i];    sort(hs+1,hs+1+n);    m = unique(hs+1,hs+1+n)-hs-1;//去重}//建树,这里建的是一颗“空树”int Build( int l, int r){    int rt = tot++;//根节点序号    c[rt] = 0;//结点代表区间含有的数的个数    if( l != r)//非叶子结点    {        int mid = (l+r)>>1;        lson[rt] = Build(l,mid);//建左右子树        rson[rt] = Build(mid+1,r);    }    return rt;//返回记录下根节点的值}//哈希查找,在hs数组中寻找x的位置int Hash( int x){    return lower_bound(hs+1,hs+1+m,x)-hs;}//更新操作,本题中每一次更新都像是新建一棵树int Update( int rt, int pos, int val){    //rt为上一棵树的根节点    int newrt = tot++;;//根节点    int tmp = newrt;//,临时记录下    c[newrt] = c[rt]+val;//该结点对应区间的值多了val个,更新个数    int l = 1, r = m;    while( l < r)    {        int mid = (l+r)>>1;        if( pos <= mid)//更新的为值在左子树        {            lson[newrt] = tot++;//新建左子树            rson[newrt] = rson[rt];//右子树就直接指向前面已知的相同区间(可持久化)            newrt = lson[newrt];            rt = lson[rt];//rt指向自己的左子树            r = mid;        }        else//更新的为右子树        {            rson[newrt] = tot++;//同理,新建右子树            lson[newrt] = lson[rt];//左子树指向已存在的            newrt = rson[newrt];//            rt = rson[rt];            l = mid+1;        }        c[newrt] = c[rt]+val;//更新对应个数    }    return tmp;//返回答案}//总的大左右区间int Query( int lrt, int rrt, int k){    int l = 1, r = m;    while( l < r)    {        int mid = (l+r)>>1;        if( c[lson[lrt]]-c[lson[rrt]] >= k)//大于k的话肯定在左边        {            r = mid;            lrt = lson[lrt];            rrt = lson[rrt];        }        else//反之,在右边        {            l = mid+1;            k -= c[lson[lrt]]-c[lson[rrt]];            lrt = rson[lrt];            rrt = rson[rrt];        }    }    return l;}int main(){    while( ~scanf("%d%d",&n,&q))    {        for( int i = 1; i <= n; i++)            scanf("%d",&a[i]);        //InitHash        InitHash();        tot = 0;        pt[n+1] = Build(1,m);        for( int i = n; i ; i--)        {            int pos = Hash(a[i]);            pt[i] = Update(pt[i+1],pos,1);        }        while( q--)        {            int l,r,k;            scanf("%d%d%d",&l,&r,&k);            printf("%d\n",hs[Query(pt[l],pt[r+1],k)]);        }    }    return 0;}


原创粉丝点击