alpc_qleonardo

ALPC_NeverGiveup……达芬奇不相信眼泪!

HDU Kth number(可持久化线段树)

Kth number

Time Limit: 15000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 14447    Accepted Submission(s): 4349


Problem Description
Give you a sequence and ask you the kth big number of a inteval.
 

Input
The first line is the number of the test cases.
For each test case, the first line contain two integer n and m (n, m <= 100000), indicates the number of integers in the sequence and the number of the quaere.
The second line contains n integers, describe the sequence.
Each of following m lines contains three integers s, t, k.
[s, t] indicates the interval and k indicates the kth big number in interval [s, t]
 

Output
For each test case, output m lines. Each line contains the kth big number.
 

Sample Input
1 10 1 1 4 2 3 5 6 7 8 9 0 1 3 2
 

Sample Output
2
 

Source


        大致题意:典型的静态区间第K大。

        之前应该是写过动态区间第K大的题目,那个是用到了线段树套平衡树Treap。虽然说支持动态修改,但是这种方法的编程复杂度和代码量实在是太大,在比赛中难以接受。在这里,我们可以考虑用可持久化线段树来解决这个问题。
        首先,关于区间的问题,要考虑如何只选取区间内部的数字。我们可以考虑初始数字1~N是按照顺序插入到主席树当中的,那么第i次操作就对应第i个数字被插入,第i棵线段树就对应一个包含第1~i个数字的线段树。那么显然,对于区间[l,r],如果我考虑第l-1棵线段树和第r棵树的差值,那么我们就可以把数字限定在这个区间里面。

        但是,按照普通线段树存储的方式,是不能够支持两棵线段树的相减操作的,而且也不便于求解第K大。于是考虑换一种存储方式。考虑一个对应区间为[l,r]的第i棵线段树节点,保存1~i中数字大小在区间[l,r]中的数字个数。那么对于对应同一数字大小区间的两棵线段树,就可以支持相减操作。对于求解第K大问题,我们先判断区间[l,r]中数字大小介于当前节点的左节点表示区间中的个数。如果大于K,那么说明第K大在左边,否在说明在右边,此时K要减去左边区间中的数字个数。一直往后直到走到单位区间。
        然后本题的话,因为不保证数据的大小范围,所以我们首先对数字进行离散化处理,对数字重新编号之后再加入主席树。具体见代码:
#include<bits/stdc++.h>
#define LL long long
#define N 150010
using namespace std;

int a[N],b[N],rt[N<<4];

struct Persistent_SegTree
{
    struct node{int l,r,sum;} T[N<<4];
    int cnt; void init(){cnt=0;}

    void build(int &i,int l,int r)
    {
        i=++cnt;
        T[i]=node{0,0,0};
        if (l==r) return;
        int mid=(l+r)>>1;
        build(T[i].l,l,mid);
        build(T[i].r,mid+1,r);
    }

    void ins(int &i,int old,int l,int r,int x)
    {
        i=++cnt;
        T[i]=T[old];
        T[i].sum++;
        if (l==r) return;
        int mid=(l+r)>>1;
        if (x<=mid) ins(T[i].l,T[old].l,l,mid,x);
               else ins(T[i].r,T[old].r,mid+1,r,x);
    }

    int query(int i,int j,int k,int l,int r)
    {
        if (l==r) return l;
        int mid=(l+r)>>1;
        int t=T[T[j].l].sum-T[T[i].l].sum;
        if (t>=k) return query(T[i].l,T[j].l,k,l,mid);
             else return query(T[i].r,T[j].r,k-t,mid+1,r);
    }

} Persist;

int main()
{
    int n,q,T_T;
    cin>>T_T;
    while(T_T--)
    {
        Persist.init();
        scanf("%d%d",&n,&q);
        Persist.build(rt[0],1,n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            b[i]=a[i];
        }
        sort(b+1,b+1+n);
        int tot=unique(b+1,b+1+n)-b-1;
        for(int i=1;i<=n;i++)
        {
            int pos=lower_bound(b+1,b+1+tot,a[i])-b;
            Persist.ins(rt[i],rt[i-1],1,n,pos);
        }
        for(int i=1;i<=q;i++)
        {
            int l,r,k;
            scanf("%d%d%d",&l,&r,&k);
            printf("%d\n",b[Persist.query(rt[l-1],rt[r],k,1,n)]);
        }
    }
    return 0;
}

阅读更多
版权声明:本文为博主原创文章,转载请著名出处 http://blog.csdn.net/u013534123 https://blog.csdn.net/u013534123/article/details/80342613
想对作者说点什么? 我来说一句

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

不良信息举报

HDU Kth number(可持久化线段树)

最多只允许输入30个字

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