可持久化线段树(待补充)

可持久化线段树初步理解(单点修改):

  当需要同时保留修改前和修改后数据时,可能就要用到可持久化数据结构。考虑线段树的单点修改,实际上只改了一部分节点的值(logn级别),如果重建一棵树,需要nlogn级别的时间和空间。此时,我们可以使用可持久化线段树,将需要被修改的节点建成新的点,并且每次修改给予线段树一个新的根,这样每次修改并且保存历史记录只用logn级别的时间,多开logn级别的空间。

我的实现:线段树使用结构体Node{

  int lson,int rson;

  int val;

};

  代码与线段树差别不大:

1.maintain和build可以无变化

2.update,每次新建节点,从原节点拷贝信息,更新之后返回新建节点的编号

 

int update(int now,int l,int r,int pos,int val)
{
    int k=tot++;
    tr[k]=tr[now];
    if (l==r)
    {
        tr[k].val+=val;
        return k;
    }
    int mid=(l+r)>>1;
    if (pos<=mid) tr[k].lson=update(tr[now].lson,l,mid,pos,val);
        else tr[k].rson=update(tr[now].rson,mid+1,r,pos,val);
    maintain(k);
    return k;
}

 

3.query,可以无变化

4.注意调用update和query的时候,选取不同的根节点就能走到不同的历史版本,而且树的结构都是一模一样的

 

模板题:求[l,r]区间第k小数,n<=100000,询问<=100000

题解:此处直接讨论离散化之后的做法(离散化后n个数为1~n)

  1、先考虑[1,n]区间的第k小数的求法,建一棵线段树,对第i个数a[i],在线段树第a[i]个位置插入1,线段树维护区间中的数的个数,找第k小数的时候直接根据线段树左半边的数的个数就可以决定往左走还是往右走

  2、考虑[l,r]区间的第k小数的求法,建(n+1)棵线段树,其中第i棵已经插入了a[1~i],找第k小数的时候同步沿着l-1和r树走,做下减法之后就得到[l,r]区间的信息,决定往左走还是往右走,当然这里用可持久化线段树,而非真的建(n+1)棵树

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;++i)
const int maxn=100010;
struct Node{
    int lson,rson;
    int val;
}tr[maxn*20];

struct PX{
    int index,val;

    operator <(const PX &b){
        return val<b.val;
    }
}px[maxn];

int tot,root[maxn],num;

void init()
{
    tot=num=0;
}

inline void maintain(int now)
{
    tr[now].val=tr[tr[now].lson].val+tr[tr[now].rson].val;
}

int build(int l,int r)
{
    int k=tot++;        //begin from 0
    if (l==r)
    {
        tr[k].val=0;
        return k;
    }
    int mid=(l+r)>>1;
    tr[k].lson=build(l,mid);
    tr[k].rson=build(mid+1,r);
    maintain(k);
    return k;
}

int update(int now,int l,int r,int pos,int val)
{
    int k=tot++;
    tr[k]=tr[now];
    if (l==r)
    {
        tr[k].val+=val;
        return k;
    }
    int mid=(l+r)>>1;
    if (pos<=mid) tr[k].lson=update(tr[now].lson,l,mid,pos,val);
        else tr[k].rson=update(tr[now].rson,mid+1,r,pos,val);
    maintain(k);
    return k;
}

int query(int lnow,int rnow,int l,int r,int kth)
{
    if (l==r) return l;
    int mid=(l+r)>>1;
    int tp=tr[tr[rnow].lson].val-tr[tr[lnow].lson].val;
    if (kth<=tp) return query(tr[lnow].lson,tr[rnow].lson,l,mid,kth);
        else return query(tr[lnow].rson,tr[rnow].rson,mid+1,r,kth-tp);
}

int n,m,T,k,l,r;
int s[maxn];
int A[maxn],ans[maxn],fans[maxn];

int main()
{
    scanf("%d",&T);
    while(T--)
    {
        init();
        scanf("%d%d",&n,&m);
        rep(i,1,n) scanf("%d",s+i),px[i].index=i,px[i].val=s[i];
        sort(px+1,px+n+1);
        A[++num]=1;ans[px[1].index]=num;fans[num]=s[px[1].index];
        rep(i,1,n) if (px[i].val==px[i-1].val) ++A[num],ans[px[i].index]=num;
                        else {A[++num]=1;ans[px[i].index]=num;fans[num]=s[px[i].index];}
        root[0]=build(1,num);
        rep(i,1,n) root[i]=update(root[i-1],1,num,ans[i],1);
        rep(i,1,m)
        {
            scanf("%d%d%d",&l,&r,&k);
            printf("%d\n",fans[query(root[l-1],root[r],1,num,k)]);
        }
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/terra/p/7577438.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值