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

    线段树(长,但是好用!)

    问题

    给你一个长度为 (n)的序列(A_1,A_2,A_3…A_n)进行如下操作:
    1.对这个序列的某个点加上一个数字vol
    2.求[l,r]区间的数字和
    看到这里,键盘侠就要开始喷了:你这打着线段树的名号讲树状数组的**(和谐)东西
    不慌,再来看第三条:
    3.求[l,r]区间的最大值和最小值
    这样的话,树状数组就不好做了吧(蒟蒻得意
    那么,进入正题,线段树!!!

    线段树好不好用

    根据某巨所言:傻逼线段树又臭又长!线段树真快真好用!

    啥叫线段树?

    回到上面这个问题,求区间和的操作用树状数组很容易就能维护,可是求区间最大最小……
    好像树状数组就不灵了?!这个时候,线段树英勇地站了出来
    首先,针对一个区间,我们可以人为地把这个区间进行划分,比如[1,5]就可以变成[1,3][4,5]两个区间合并的结果
    那么,每次把一个区间进行二分,就……就得到了一棵二叉树!
    [1,5]
    [1,3][4,5]
    [1,2][3,3][4,4][5,5]
    [1,1][2,2]

    void build(int p,int l,int r)
    {
         if(l==r)return ;//叶结点
         int mid=(l+r) / 2;
         build(p*2, l, mid),build(p*2+1, mid+1, r);
    }
    //线段树建树模板
    

    线段树怎么用?

    既然我们已经有了一个个区间了,那么我们就要做点有意义的事情
    Such as:开一个sum前缀和数组,mx最大值数组,mn最小值数组
    可是,这些数组该怎么维护呢?

    [if(l==r)return ; ]

    这一句空空如也,盘他!

    if(l==r)
    {
       sum[p]=a[l];
       mx[p]=a[l];
       mn[p]=a[l];
       return ;
    }
    

    这时,蒟蒻脑袋里蹦出了一个problem:树的叶结点改变,那父结点呢?
    这可怎么办?
    不慌,因为线段树是二叉树结构,所以我们直接提取ta儿子的信息就行了

    void update(int p)
    {
         sum[p]=sum[p*2]+sum[p*2+1];
         mx[p]=max(mx[p*2],mx[p*2+1]);
         mn[p]=min(mn[p*2],mn[p*2+1]);
    }
    

    单点修改

    现在知道维护了以后,那么就要开始第一个操作:修改单点值
    修改单点的思想其实就是搜索(l=r=x)的一个叶结点,但是要注意修改后的区间信息要重新丢掉update()里面去维护一下

    void change(int p,int l,int r,int x,int v)
    {
          if(l==r)
          {
             sum[i]+=v;
             return ;
          }
          int mid=(l+r)/2;
          if(x<=mid)change(p*2,l,mid,x,v);
          else      change(p*2+1,mid+1,r,x,v);
          update(p);
    }
    //单点修改前缀和模板
    

    区间查询

    看了上面这么多,有一个致命的问题:区间没有全列举到!!!
    wow,这个就很难办,要是无法处理,线段树彻底成为废物
    but区间是可以拆分和合并的……
    还是那个[1,5]的区间,现在我们要拿出[1,4]这个区间的信息:
    [1,5]
    [1,3][4,5]
    [1,2][3,3][4,4][5,5]
    [1,1][2,2]
    显然我们要用的是线段树中[1,3][4,4]两个节点,他们又都是[1,4]的一部分……
    等等?![1,4]的一部分!!!(垂死病中惊坐起)

    int query(int p,int l,int r,int x,int y)
    //[x,y]是我们要找的区间
    {
         if(l>=x&&r<=y)
            return sum[p];//调取的信息
         int mid=(l+r)/2,ans;//ans代表这个区间某个值(前缀和,最大最小值等……)
         if(x<=mid)ans+=query(p*2,l,mid,x,y);
         if(y>mid)ans+=query(p*2+1,mid+1,r,x,y);
         return ans;
    }
    //前缀和调取模板
    

    ------------恢复内容结束------------

  • 相关阅读:
    Laravel 底层原理:门面(Facades)
    用 PHP和Golang 来刷leetCode 之 无重复字符 最长子串
    今天发现一个好用的查询IP地址的工具,记录一波
    Ubuntu下安装SDL
    敏捷宣言
    python 读取xml文档
    每个程序员都必须遵守的编程原则
    作为Web开发人员,我为什么喜欢Google Chrome浏览器
    爬虫
    Python为什么要self
  • 原文地址:https://www.cnblogs.com/jyx-txzr/p/13192996.html
Copyright © 2011-2022 走看看