zoukankan      html  css  js  c++  java
  • 线段树

    1.线段树是什么?

    基于分治思想的二叉树结构,用于区间操作。

    2.主要操作(例子是:线段树维护区间最大值)

    • 建树

      一点点分着向下拓展,左儿子编号是父节点编号*2,右儿子编号是左儿子编号+1;

    void create(int fa,int l,int r,long long t[])
    {
        stree[fa].lazy=0;
        if(l==r)
            stree[fa].val=t[l];
        else
        {
            int mid=(l+r)/2;
            create(2*fa,l,mid,t);
            create(2*fa+1,mid+1,r,t);
            stree[fa].val=stree[2*fa].val+stree[2*fa+1].val;
        }
    }
    • 单点修改

      像建树时的一点点拓展一样,我们需要一直二分找到该点,并且把这个点父节点和祖父节点的所有区间都更新 

    void change (int fa,int x,int v)
    {
         if(stree[fa].l==stree[fa].r)
         {
                stree[fa].val=v;
                return;
         }   
          int mid=(stree[fa].l+stree[fa].r)>>1;
          if(x<=mid) change(fa*2,x,v);
          else change(fa*2+1,x,v);
          stree[fa].val=max(stree[fa*2].val,stree[fa*2+1].val);
        
    }            
    • 区间查询

      查询 区间【l,r】的最大值;

      1.若【l,r】完全覆盖了当前节点代表的区间,立即回溯,并且该节点的val为候选

      2.若左端点与【l,r】有重叠部分,就递归进入左儿子

      3.同理,若右端点与【l,r】有重叠部分,就递归进入右儿子

    int askaskask(int fa,int l,int r)
    {
        if(l<=stree[fa].l&&r>=stree[fa].r)
            return stree[fa].val;
        int mid=(stree[fa].l+stree[fa].r)>>1;
        int ans=-0x3f3f3f3f
        if(l<=mid)
            ans=max(ans,askaskask(fa*2,l,r));
        else 
            ans=max(ans,askaskask(fa*2+1,l,r));
        return ans;   
    }
    • 区间加和区间乘(需要延迟标记和下放操作)

      如果我们进行区间加的时候,区间内每一个都加,还是要递归进行,每个子节点都会被更新到,那么复杂度就从o(logn)上升到o(n)了,我们肯定不想这种情况出现,所以我们有了

    延迟标记(只在要修改的区间标记上我们干啥了,先不像单点修改那样更新父节点),顾名思义,延迟标记的作用是让我们只有在用的时候才把它下放,标记的时候是o(1),下放的时候是o(logn)的,保证了插入的时间复杂度;

      那么问题来了,我们怎么下放???

      直接看代码,简单的只有加和只有乘你们通过这个既有加又有乘的例子自行体会。

    void shutdown(int root,int l,int r)//下放
    {
        if(point[root].mull!=1)//先下放乘法标记,再下放加法标记;
        {
            point[2*root].addl=(point[root].mull*point[2*root].addl)%p;
            point[2*root].mull=(point[root].mull*point[2*root].mull)%p;
            point[2*root+1].addl=(point[root].mull*point[2*root+1].addl)%p;
            point[2*root+1].mull=(point[root].mull*point[2*root+1].mull)%p;
            point[2*root].value=(point[2*root].value*point[root].mull)%p;
            point[2*root+1].value=(point[2*root+1].value*point[root].mull)%p;
            point[root].mull=1; //下放完记得清空标记;
        }
            int mid=(l+r)>>1;
            point[2*root].addl+=point[root].addl;
            point[2*root].value+=point[root].addl*(mid-l+1);
            point[2*root+1].addl+=point[root].addl;
            point[2*root+1].value+=point[root].addl*(r-mid);
            point[root].addl=0;
    }
    void add(int root,int l,int r,int ll,int rr,int plus)//区间加
    {
        if(l>rr||r<ll)
            return ;
        else if(l>=ll&&r<=rr)
        {
            point[root].addl+=plus;
            point[root].value+=plus*(r-l+1);
            return ;
        }
        else
        {
            int mid=(l+r)>>1;
            shutdown(root,l,r);
            add(2*root,l,mid,ll,rr,plus);
            add(2*root+1,mid+1,r,ll,rr,plus);
            point[root].value=point[2*root].value+point[2*root+1].value;
        }
    }
    void mul(int root,int l,int r,int ll,int rr,int multiply)//区间乘
    {
        if(l>rr||r<ll)
            return ;
        else if(l>=ll&&r<=rr)
        {
            point[root].addl=(point[root].addl*multiply)%p;
            point[root].mull=(point[root].mull*multiply)%p;
            point[root].value=(point[root].value*multiply)%p;
            return ;
        }
        else
        {
            int mid=(l+r)>>1;
            shutdown(root,l,r);
            mul(2*root,l,mid,ll,rr,multiply);
            mul(2*root+1,mid+1,r,ll,rr,multiply);
            point[root].value=point[2*root].value+point[2*root+1].value;
        }
    }

    3.注意事项

    如果是一个有N个叶子节点的二叉树,共有2n-1个节点,又因为我们用父子二倍的节点编号,所以保存线段树的数组长度不小于4N才能保证不越界;

    可以做两道模板题玩一玩:洛谷p3372 p3373

    还有一道我觉得很神,巨佬们觉得很水的题:洛谷p4198 楼房重建。(其实考的就是思路)

  • 相关阅读:
    phpspider爬虫框架的使用
    【php设计模式】责任链模式
    【php设计模式】策略模式
    【php设计模式】观察者模式
    【php设计模式】模板模式
    【温故知新】php 魔术方法
    【php设计模式】享元模式
    Java50道经典习题-程序18 乒乓球赛
    Java50道经典习题-程序19 输入行数打印菱形图案
    Java50道经典习题-程序20 求前20项之和
  • 原文地址:https://www.cnblogs.com/royal-8/p/9259470.html
Copyright © 2011-2022 走看看