HDU 2665 Kth number 可持久化线段树

题意:给n个数和m个询问,询问l,r,k是从l~r中的第k小
思路:可持久化线段树的模板题
说下自己对可持久化线段树的理解吧
可持久化线段树的是可以保存历史版本的线段树,就是插进去第i个数的线段树的状态,这样我们可以通过state[r]-state[l-1]来得到state[l~r]
朴素做法就是维护n颗线段树,但是这样一般都会MLE
可持久化线段树利用了每次插入数只修改了线段树上一条链的特性来每次插入一个数只新建一条链来维护历史版本,空间复杂度O(n*logn+n*logn) 原树+新建的链
这样我们可以通过上述做法在得到state[l~r]

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<math.h>
#include<queue>
#include<stack>
#include<string>
#include<vector>
#include<map>
#include<set>
using namespace std;
#define lowbit(x) (x&(-x))
typedef long long LL;
#define maxn 100005
const int inf=(1<<28)-1;
struct Seg_node
{
    int lson,rson,sum;
};
int cmp(int a,int b)
{
    return a>b;
}
class Segment
{
    public:
    int A[maxn],h[maxn];//原数组和hash后的数组
    int Size1,Size2;//原数组和hash后的数组大小
    int root[maxn];//root[i]表示第i棵树的根节点
    int tot;//节点个数 
    Seg_node seg[maxn*20];//树的节点
    void init()
    {
        tot=0;
    }
    void hash_array(int chs=0)//chs=0从小到大 
    {
        for(int i=1;i<=Size1;++i)
        h[i]=A[i];
        if(chs) sort(h+1,h+Size1+1,cmp);
        else sort(h+1,h+Size1+1);
        Size2=1;
        for(int i=2;i<=Size1;++i)
        if(h[i]!=h[i-1]) h[++Size2]=h[i];
    }
    void build(int l,int r,int& rt)//因为要给每个节点找左右儿子,所以& 
    {
        rt=++tot;
        seg[rt].sum=0;
        if(l==r) return ;
        int mid=(l+r)/2;
        build(l,mid,seg[rt].lson);
        build(mid+1,r,seg[rt].rson);
    }
    void update(int last,int k,int l,int r,int& rt)
    {
        rt=++tot;
        seg[rt]=seg[last];
        seg[rt].sum++;
        if(l==r) return ;
        int mid=(l+r)/2;
        if(k<=mid) update(seg[last].lson,k,l,mid,seg[rt].lson);
        else update(seg[last].rson,k,mid+1,r,seg[rt].rson);
    }
    int query(int rt1,int rt2,int l,int r,int k)
    {
        if(l==r) return l;
        int cnt=seg[seg[rt2].lson].sum-seg[seg[rt1].lson].sum;
        int mid=(l+r)/2;
        if(k<=cnt)
        return query(seg[rt1].lson,seg[rt2].lson,l,mid,k);
        else
        return query(seg[rt1].rson,seg[rt2].rson,mid+1,r,k-cnt);
    }
    int find(int x)
    {
        int l=1,r=Size2;
        while(l<=r)
        {
            int mid=(l+r)/2;
            if(h[mid]==x) return mid;
            if(h[mid]>x) r=mid-1;
            else l=mid+1;
        }
        return l;
    }
    void fun()//更新1~n的点的链 
    {
        init();
        hash_array();
        build(1,Size2,root[0]);
        for(int i=1;i<=Size1;++i)
        {
            int pos=find(A[i]);
            update(root[i-1],pos,1,Size2,root[i]);
        }
    }
    int Get_k(int l,int r,int k)
    {
        int tmp=query(root[l-1],root[r],1,Size2,k);
        return h[tmp];
    }
}Seg;
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        Seg.Size1=n;
        for(int i=1;i<=n;++i)
        scanf("%d",&Seg.A[i]);
        Seg.fun();
        for(int i=1;i<=m;++i)
        {
            int l,r,k;
            scanf("%d%d%d",&l,&r,&k);
            printf("%d\n",Seg.Get_k(l,r,k));
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值