可持久化线段树(prizident tree)学习笔记

一、定义:“可持久化”定义:可以支持回退,访问之前版本的数据结构;

     主席树:可以访问未经过其他操作的版本的线段树

二、原理:

主席树与线段树的基本操作相同,唯一的难点在于如何实现可持久化。

如果想要访问每个版本的线段树,首先想到的是对线段树进行全盘复制,然后在上个版本的基础上进行本次操作以建立新版本。但很显然如此一来空间复杂度会直接窜到O(n*m),显然不可行

考虑操作对线段树的作用:每次操作一定不会涉及线段树上所有的节点,而不涉及的节点必然在两个版本中是相同的,也就是可以共享的

以此为切入点,如果我们在每次进行操作时只修改被影响的节点,可以得到如下的结果(洛咕借的图)

 

具体的做法是,进行每次操作时复制上个版本的根作为新版本的根,再递归判断修改的子节点进行修改

代码:

inline int update(int pre, int l, int r, int x)
{
    int rt=++cnt;
    L[rt]=L[pre]; R[rt]=R[pre]; sum[rt]=sum[pre]+1;
    if (l<r)
    {
        if (x <= mid) L[rt] = update(L[pre], l, mid, x);
        else R[rt]=update(R[pre], mid+1, r, x);
    }
    return rt;
}

查询时直接调用版本对应的根就可以咯


洛咕板子:查询静态区间第k小

与主席树的联系:把序列从左到右插入到树中,没插入一次对应一个版本,在l——r间查询对应在版本l和r间进行比较查询

建树方法:先把整个序列进行离散化,线段树维护每个数出现过多少次

比较方法:导入两个根,先对两者左儿子中数的个数进行比较,若比k小则说明k在右儿子中,进行递归

完整代码:

 

#include<cstdio>
#include<algorithm>
#define maxn 200010
#define mid (l+r)/2
using namespace std;
int ls[maxn<<5],rs[maxn<<5],sum[maxn<<5];
int a[maxn],b[maxn],cnt=0,t[maxn<<5];
int n,m,q;
int build(int l,int r)
{
    int rt=++cnt;
    sum[rt]=0;
    if(l<r)
    {
        ls[rt]=build(l,mid);
        rs[rt]=build(mid+1,r);
    }
    return rt;
}
int update(int pre,int l,int r,int k)
{
    int rt=++cnt;
    ls[rt]=ls[pre];
    rs[rt]=rs[pre];
    sum[rt]=sum[pre]+1;
    if(l<r)
    {
        if(k<=mid)
            ls[rt]=update(ls[pre],l,mid,k);
        else
            rs[rt]=update(rs[pre],mid+1,r,k);
    }
    return rt;
}
int query(int u,int v,int l,int r,int k)
{
    if(l>=r)
        return l;
    int usm=sum[ls[v]]-sum[ls[u]];
    if(k<=usm)
        return query(ls[u],ls[v],l,mid,k);
    else
        return query(rs[u],rs[v],mid+1,r,k-usm);
}
int main()
{
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++)    
    {
        scanf("%d",&a[i]);
        b[i]=a[i];
    }
    sort(b+1,b+n+1);
    m=unique(b+1,b+n+1)-b-1;
    t[0]=build(1,m);
    for(int i=1;i<=n;i++)
    {
        int p=lower_bound(b+1,b+m+1,a[i])-b;
        t[i]=update(t[i-1],1,m,p);
    }
    while(q--)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        printf("%d\n",b[query(t[x-1],t[y],1,m,z)]);
    }
    return 0;
}
    

 

posted @ 2019-07-25 13:00  .Terena  阅读(287)  评论(1编辑  收藏  举报