• 可持久化线段树(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
    ",b[query(t[x-1],t[y],1,m,z)]);
        }
        return 0;
    }
        
  • 相关阅读:
    Java文件流应用:复制文件
    Java IO流之文件流
    初识Java-IO流
    Java集合之Properties
    Java之FilenameFilter接口
    Java之File类
    Java内部类
    Java常用类之要点总结
    Java异常类(Throwable)
    php分布式缓存系统 Memcached 入门
  • 原文地址:https://www.cnblogs.com/charlesss/p/11243589.html
Copyright © 2020-2023  润新知