zoukankan      html  css  js  c++  java
  • 神奇线段树模板+练习

    看到hjw初一神线段树辣么6,于是默默又去复(xue)习(xi)了一下线段树。

    线段树大概的原理什么的就不讲了。放个模板就好。(可能注释会比较多)

    我线段树有个风格... 先说下   i<<1 的意思是 i*2  

                                         (i<<1) or 1 的意思是 i*2+1

    注释有错麻烦说下~~~

    type
       node=record
            l,r:longint;//节点区间[l,r]
            sum:int64;//节点的信息,这里指和
            flag:longint;//懒惰标记
       end;
    var
        tree:array[0..800000]of node;//线段树
        ans:int64;//答案
        i:longint;
        x,d,a,b,p:longint;
        n,m:longint;
    procedure changesum(i:longint);//上推过程更改节点的信息
    begin
      tree[i].sum:=tree[i<<1].sum+tree[i<<1 or 1].sum;
    end;
    
    procedure build(i:longint;l,r:longint);//建树,以节点i为根,l,r是总区间
    var m:longint;//二分思想的中间值
    begin
      tree[i].l:=l;
      tree[i].r:=r;
      if tree[i].l=tree[i].r then  //到达叶子节点,可以回溯了
      begin
        exit;
      end;
      m:=(l+r)>>1;//中间值
      build(i << 1,l,m); //向左儿子建树
      build((i << 1) or 1,m+1,r);//向右儿子建树
      changesum(i);//回溯更改节点信息
    end;
    
    procedure down(i:longint);//下推
    var l,r,lnum,rnum:longint;
    begin
      l:=i<<1; //左儿子
      r:=i<<1 or 1; //右儿子
      lnum:=tree[l].r-tree[l].l +1; //左儿子包含的数的个数
      rnum:=tree[r].r-tree[r].l +1; //右儿子包含的数的个数
      inc(tree[l].flag,tree[i].flag); //向左儿子下推懒惰标记
      inc(tree[r].flag,tree[i].flag); //向右儿子下推懒惰标记
      inc(tree[l].sum,tree[i].flag*lnum); //更新左儿子信息
      inc(tree[r].sum,tree[i].flag*rnum); //更新右儿子信息
      tree[i].flag :=0; //懒惰标记清零
    end;
    
    procedure aski(i:longint;x:longint); //单点查询, i 为当前节点, x 为查询的下标, 
    var m:longint;
    begin
      if tree[i].l=tree[i].r then //到叶子节点,可以返回值
      begin
        ans:=tree[i].sum; //储存在 ans(全局变量) 中
        exit;
      end;
      if tree[i].flag<>0 then down(i); //有懒惰标记要下推
      m:=(tree[i].l+tree[i].r)>>1; //中间值
      if x<=m then aski(i<<1,x) else aski(i<<1 or 1,x); //二分查找 x
    end;
    
    procedure changei(i,d,x:longint); //单点修改, i 为当前节点, d 为修改值, x 为修改的下标
    var m:longint;
    begin
      if tree[i].l=tree[i].r then //到叶子节点,可以修改了
      begin
        inc(tree[i].sum,d);
        exit;
      end;
      m:=(tree[i].l+tree[i].r)>>1;
      if x<=m then changei(i<<1,d,x) else changei(i<<1 or 1,d,x); //查找 x 节点
      changesum(i); //回溯更改节点信息
    end;
    
    procedure asklr(i:longint;a,b:longint); //查询区间 i 为当前节点 a,b 为查询的[l,r]区间
    var m:longint;
    begin
      if (tree[i].l>=a)and(tree[i].r<=b) then //有一个节点的区间能包含[l,r](这里的 a,b )区间
      begin
        inc(ans,tree[i].sum);
        exit;
      end;
      if tree[i].flag<>0 then down(i); //下推咯
      m:=(tree[i].l+tree[i].r)>>1;
      if a<=m then asklr(i<<1,a,b);
      if b>m then asklr(i<<1 or 1,a,b);
    end;
    
    procedure changelr(i,a,b,d:longint); //更改区间  i为当前节点   a 为 l   b 为 r   d 为修改值
    var m:longint;
    begin
      if (tree[i].l>=a)and(tree[i].r<=b) then
      begin
        inc(tree[i].sum,(tree[i].r-tree[i].l+1)*d);
        inc(tree[i].flag,d); //记得懒惰标记咯
        exit;
      end;
      if tree[i].flag<>0 then down(i); //下推咯
      m:=(tree[i].l+tree[i].r)>>1;
      if a<=m then changelr(i<<1,a,b,d);
      if b>m then changelr(i<<1 or 1,a,b,d);
      changesum(i);
    end;
    
    begin
      build(1,1,n); // 以1为根,  [l,r]区间就是[1,n]
    aski(1,x); // 从根节点 1 开始找, 查询为 x 下标的信息
    changei(1,d,x); //从根节点 1 开始找, 修改值为 d , 修改 x 下标
    asklr(1,l,r); //从根节点 1 开始找, 查询 l,r 区间
    changelr(1,l,r,d) //从根节点 1 开始找 修改 l,r 区间 修改值d
    end.

    练习什么的以后再来。

  • 相关阅读:
    ios swift 判断uiviewcontroller时push present 进来的 还是pop进来的
    vue wangeditor3封装
    ios uiimagepickercontroller 选择相册或者拍照上传
    ios 监控键盘状态
    ios 真机使用相机闪退问题
    swift bannerview 广告轮播图
    ios avplayer 监控播放进度
    【C++】智能指针
    [LeetCode] Word Break
    【海量数据处理】100亿个整数,内存足够,如何找到中位数?内存不足,如何找到中位数?
  • 原文地址:https://www.cnblogs.com/Bunnycxk/p/6501861.html
Copyright © 2011-2022 走看看