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

Posted 心里只有学习

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了可持久化线段树(待补充)相关的知识,希望对你有一定的参考价值。

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

  当需要同时保留修改前和修改后数据时,可能就要用到可持久化数据结构。考虑线段树的单点修改,实际上只改了一部分节点的值(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;
}

 

以上是关于可持久化线段树(待补充)的主要内容,如果未能解决你的问题,请参考以下文章

可持久化线段树

可持久化线段树(主席树)

待更新算法

洛谷P3402 模板可持久化并查集(可持久化线段树,线段树)

线段树模板(待补充)

线段树入门+例题