线段树 O(nlogn)

  • 我们通常会遇到维护一个序列(区间)的问题,如区间修改,区间查询,单点修改,单点查询。
  • A:我会暴力!
  • B:我会分块!!
  • 可是题目数据是个出题人就会卡暴力,只要1e5就轻松卡掉你。有人说他可以手动开O1,O2,O3,那都是弟弟们的做法。所以我们正式引入数据结构--线段树。

线段树

  • 线段树是一棵二叉树,只是其中每个节点对应的是一段区间。

  • 显然根节点对应的区间是[0,n-1],若一个点对应的是[l,r],则他的左右儿子应是[l,mid],[mid+1,r]这类似于一个二分的过程,叶子节点的l=r。我们也可以看出最后一层有n个节点[1,1]~[n,n],在往上则是n/2个节点以此类推线段树上一共2*n-1个节点。线段树的高是O(logn)级别的,当我们需要维护序列长度为2的整次幂时,线段树是一个满线段树,否则它的前h-1行时满线段树而最后一行可能不是。

普及完了概念性的东西我们步入正题

  • 初始建树

    • 线段树的每个节点都维护了所对应区间的最小值,我们可以用简单的递归完成这棵初始线段树。
    • bulid(k,l,r)表示区间[l,r]的线段树k,若l==r则我们能直接构造一个叶子节点区间最小值是a1否则我们进行递归build(k*2,l,mid)和build(k*2+1,mid+1,r)。它的最小值就是两个儿子节点中的min值。这个过程是O(n)级别的,背下来不解释。还有数组要开到4n以防卡炸。
    • int t[N];
      void build(int k,int l,int r)
      {
          if(l==r)
          {
              t[k]=v;
              return;
          }
          int min=(l+r)/2;
          build(2*k,l,mid);
          build(2*k+1,mid+1,r);
          t[k]=min(t[k*2],t[2*k+1]);
      }
      ......
      Build Code

      建树过。。。qaq

  • 单点修改

    • void update(int o,int l,int r,int ind,int ans)
      {
          if(l==r)
          {
               st[o]=ans;
               return;
           }
           int m=l+((r-l)>>1);
           if(ind<=m)
           {
               update(o<<1,l,m,ind,ans);
           }
           else{
               update((o<<1)|1,m+1,r,ind,ans);
           }
           st[o]=max(st[o<<1],st[(o<<1)|1]);
      }
      ......
      update(1,1,n,ind,ans);
      Point Code

      更新一个值时我们需要把包含着个点的节点全部更新一次。我们可以直接修改如果递归到了叶子节点。否则更新左右子节点区间最小值较小的即可。

    • int t[N];
      void updata(int k,int l,int r,int x,int r)
      {
          if(r<x||l>x)return;
          if(l==r&&l==x)
          {
              t[k]=v;
              return;
          }
          int mid=(l+r)/2;
          updata(k*2+1,mid+1,r,x,v);
          updata(k*2,l,mid,x,v);
          t[k]=min(t[k*2],t[k*2+1]);
      }
      View Code

      updataupdataupdata~qaq

  • 区间查询

 

posted @ 2019-07-31 10:29  milkitblogcn  阅读(222)  评论(0编辑  收藏  举报