zoukankan      html  css  js  c++  java
  • 动态开点线段树——节约资源,你值得拥有

    简介:

    发现,有的时候,线段树需要维护的区间很大很大,但是实际用到的节点很少很少。

    那么,我们干脆就不要开这么多的节点,用到的时候再向内存要。

    也就是说,我们建立了一棵残疾的线段树,缺少很多枝叶,但是绝对够用了。

    画个图大概理解一下(虽然也不太对)

    实心边框的点都是我们申请内存给的,虚的点是没用的。就算申请也不用,实在是浪费资源。

    所以,

    我们开局只有一个根,

    装备全靠给。

    枝叶全靠给。

    例如我们要建立一个权值线段树,但是在线操作不让你离散化,值域又是inf级别的,

    像这样,即使这个区间的范围很大,但是如果询问q比较少的话,我们只需要qloginf个节点,就可以办到。

     

    具体代码实现:

    不同的操作,但是大同小异。

    还是类似于一般线段树的。

     

    框架:

    function(int &x,int l,int r,int blablabla){
        if(!x){
            //建新节点,并处理信息
            if(l==r) //叶子节点由于是真正要用的节点(对于单点),往往还要特殊记录信息 
        }
        if(blablabla)  return ???//判断是否能返回等等
        (int ret)  // 如果需要返回时停留更新信息,就弄一个ret 
        if(blablabla) return (t[x].ls,l,mid,blablabla)
        if(blablabla) return (t[x].rs,mid+1,r,blablabla);
        (pushup(x)) //回溯后更新 
    }

    发现和主席树有点像,但是省空间的思想还是有些不同的。

    主席树是:多棵线段树,利用相邻之间有很大部分是相同的。可以在之前线段树基础上建立线段树。

        特点:许多线段树共用儿子节点

    动态开点线段树:一棵线段树,利用实际用到的点不多,少开了很多节点。

        特点:区间范围很大(通常不能直接开下)

    共同点:(都是线段树)

        都通过新加入的节点有限,进行的空间优化。使得时间空间复杂度都是logn/次

    例题:(里面也有本篇的部分讲解)

    NOIP2017 列队

    upda:2018.9.22

    主席树相邻的有很大的关系,那么动态开点线段树呢?

    动态开点线段树也可以支持合并。

    函数:

    int merge(int x,int y,int l,int r){
        if(!x||!y) return x|y;
        if(l==r){
            do something on the leaf
        }
        else{
        t[x].ls=merge(t[x].ls,t[y].ls,l,mid);
        t[x].rs=merge(t[x].rs,t[y].rs,mid+1,r);
        pushup(x);
        }
        return x;
    }

    理解:空节点直接返回,然后本质其实是相当于利用y的儿子们,修改x儿子们的信息。

    同样也是会达到共用儿子的目的。

    注意叶子节点的特判暴力合并。

    其实类似左偏树的合并。

    但是复杂度的原理证明不太相同。

    这个merge的证明主要是通过,每成功合并一次,节点数会少1个。

    如果开始有mlogn个节点,那么最多合并mlogn次,复杂度就可以保证。

    空间复杂度:总共nlogn,也就是开始节点个数。

    发现,其实我们把动态开点线段树和主席树联系起来了!

    但是,主席树不能直接合并多棵(其实也可以,但就和动态开点线段树没区别了。),必须在一棵基础上建立,而且不能灵活支持修改。

    动态开点线段树就可以支持一次次地合并多棵为一棵,但是同样,合并后,再修改就比较麻烦了。

    例题:

    [Vani有约会]雨天的尾巴——树上差分+动态开点线段树合并

    upda:2018.11.1

    根据上面两个题的做法,

    动态开点线段树,还经常开很多棵,

    对于某个点要打很多不同的标记,并且父子之间标记要快速合并的话,

    那么每个点动态开点线段树就比较优秀了。

    (相较于一般的差分只是差分一种标记,这个就比较强大了。)

    甚至:

    天天爱跑步——树上差分

    这个题用个动态开点线段树直接打差分标记也是可以的。。。(如果你不会全局桶的骚操作的话)

  • 相关阅读:
    findall查找 ^$*+?{ }{m,n}[].[.] w s d  D W
    find查找、split分隔、replace替换
    round四舍五入
    pow求一个数的n次幂
    iter创建一个可以被迭代的对象
    notepad++ gmt中文乱码问题
    matlab eps 字体用AI打开乱码的解决
    [转载]Matlab中使用xlswrite函数时出现服务器出现异常的解决方法
    How to determine which grid cells a line segment passes through?
    matlab给定点生成多边形,多边形掩膜处理
  • 原文地址:https://www.cnblogs.com/Miracevin/p/9582486.html
Copyright © 2011-2022 走看看