首页 > 其他分享 >线段树 复习笔记

线段树 复习笔记

时间:2023-01-14 21:11:06浏览次数:45  
标签:复习 int 线段 tree mid 笔记 区间 lz sum

线段树是一类处理区间问题的优秀算法,通过用空间换时间来得到相对平均的复杂度。
同时,也是一个 OIer 从萌新到提高的重要过渡。

1.线段树的基本概念

不难看出线段树是一棵完全二叉树,故我们可以用一维数组存整棵树。
显然,根节点的值等于左右两个节点之和,即
\(tree[i].sum = tree[i * 2].sum + tree[i * 2 + 1].sum\)
建树方法:递归建树。

inline void build(int i, int l, int r){
  tree[i].l = l, tree[i].r = r; 
  if(l == r){
    tree[i].sum = input[l]; 
    return ; 
  } 
  int mid = (l + r) >> 1; 
  build(i * 2, l, mid), build(i * 2 + 1, mid + 1, r);
  tree[i].sum = tree[i*2].sum + tree[i*2+1].sum; 
}

2.线段树基本操作
(1)区间查询
分三种情况:
1.查询区间在同一个节点,直接返回。
2.查询区间与这个节点有交集,取 mid ,r 小于递归左边,l 大于递归右 边,否则两边都要递归。
3.查询区间与这个节点交集为空,不存在,跳过。

inline int query(int i, int l, int r){ 
  if(tree[i].l >= l && tree[i].r <= r) return tree[i].sum; 
  if(tree[i].r < l || tree[i].l > r) return 0;
  int s = 0; 
  if(tree[i*2].r >= l) s += query(i * 2, l, r);
  if(tree[i*2+1].l <= r) s += query(i * 2 + 1, l, r);
  return s; 
}

(2)单点修改

修改这个区间的单点,相对简单很多,把区间的第 dis 位加上 k 。从根节点开始,看这个 dis 是在左儿子还是在右儿子,在哪往哪跑,返回的时候还是按照 tree[i].sum=tree[i2].sum+tree[i2+1].sum 更新所有路过点。

inline void add(int i, int dis, int k){ 
  if(tree[i].l == tree[i].r){//如果是叶子节点,那么说明找到了 
    tree[i].sum += k; 
    return ; 
  } 
  if(dis <= tree[i * 2].r) add(i * 2, dis, k);//在哪往哪跑 
  else add(i * 2 + 1, dis, k); 
  tree[i].sum = tree[i * 2].sum + tree[i * 2 + 1].sum;//返回更新 
  return ; 
}
(3)区间修改
区间修改和单点查询,我们的思路就变为:如果把这个区间加上 k ,相当于把这个区间涂上一个 k 的标记,然后单点查询的时候,就从上跑道下,把沿路的标记加起来就好。这里面给区间贴标记的方式与上面的区间查找类似,原则还是那三条,只不过第一条:如果这个区间被完全包括在目标区间里面,直接返回这个区间的值变为了如果这个区间如果这个区间被完全包括在目标区间里面,讲这个区间标记 k 
```cpp
void modify(int p, int l, int r, int k) { 
  if(tr[p].l >= l && tr[p].r <= r) { 
    tr[p].num += k; 
    return ; 
  } 
  int mid = tr[p].l + tr[p].r >> 1; 
  if(l <= mid) modify(p << 1, l, r, k); 
  if(r > mid) modify(p << 1 | 1, l, r, k); 
}

(4)单点查询

void query(int p, int x) { 
  ans += tr[p].num;
  if(tr[p].l == tr[p].r) return; 
  int mid = tr[p].l + tr[p].r >> 1; 
  if(x <= mid) query(p << 1, x); 
  else query(p << 1 | 1, x); 
}

3.线段树进阶操作

“懒标记” lazytag ,来记录这个区间,修改的时候,按照如下原则:
1、如果当前区间被完全覆盖在目标区间里,讲这个区间的 sum+k(tree[i].r-tree[i].l+1)
2、如果没有完全覆盖,则先下传懒标记
3、如果这个区间的左儿子和目标区间有交集,那么左儿子,如果右儿子和目标区间有交集,那么搜索右儿子
注意处理完这些以后,还是要按照
tree[i].sum=tree[i
2].sum+tree[i*2+1].sum的原则返回。

void push_down(int i) { 
  if(tree[i].lz != 0){ 
    tree[i*2].lz += tree[i].lz;
    tree[i*2+1].lz += tree[i].lz; 
    int mid = (tree[i].l + tree[i].r) / 2;
    tree[i*2].sum += tree[i].lz * (mid - tree[i * 2].l + 1);
    tree[i * 2 + 1].sum += tree[i].lz * (tree[i * 2 + 1].r - mid); 
    tree[i].lz = 0;
  } 
  return ; 
}

void add(int i, int l, int r, int k) { 
  if(tree[i].r <= r && tree[i].l >= l){ 
    tree[i].sum += k * (tree[i].r - tree[i].l + 1); 
    tree[i].lz += k;//记录lazytag 
    return ; 
  } 
  push_down(i);//向下传递 
  if(tree[i * 2].r >= l) add(i * 2, l, r, k); 
  if(tree[i * 2 + 1].l <= r) add(i * 2 + 1, l, r, k);
  tree[i].sum = tree[i * 2].sum + tree[i * 2 + 1].sum; 
  return ; 
}

inline int search(int i, int l, int r){ 
  if(tree[i].l >= l && tree[i].r <= r) return tree[i].sum; 
  if(tree[i].r < l || tree[i].l > r) return 0; 
  push_down(i); 
  int s = 0; 
  if(tree[i * 2].r >= l) s += search(i * 2, l, r); 
  if(tree[i * 2 + 1].l <= r) s += search(i * 2 + 1, l, r); 
  return s; 
}

标签:复习,int,线段,tree,mid,笔记,区间,lz,sum
From: https://www.cnblogs.com/sunruize/p/17052552.html

相关文章

  • 笔记本在使用一段时间后莫名卡顿的解决方法——CPU莫名锁频
    首先要看看是不是因为CPU锁频了。键盘按下Ctrl+Shift+Esc,看看CPU内的速度部分是否保持在零点几。解决方案如果是的话,可以先将电脑关机,然后将所有的外设拔掉,电源也拔掉......
  • Google_Book_20Things.前言以及前四项学习笔记
    20THINGSILEARNEDABOUTBROWSERSANDTHEWEBIllustratedbyChristophNiemann.WrittenbytheGoogleChromeTeam.关于浏览器与网络的20件事前言(Foreword)任何......
  • 线段树优化建图学习笔记
    使用场景:单点向区间,区间向单点或区间向区间建边。实现原理:用线段树的一个节点管辖一段图上区间的顶点。实现步骤:将原图中的顶点拆点(理论上,实际代码中不需要),出点和入点......
  • Redis 6 学习笔记 3 —— 用SpringBoot整合Redis的踩坑,了解事务、乐观锁、悲观锁
    SpringBoot整合Redis时踩到的坑jdk1.8环境,用idea的SpringInitializr创建springboot项目,版本我选的2.7.6。pom文件添加的依赖如下,仅供参考。注意commons-pool2选错版本......
  • vue-cli笔记
    一、安装全局安装npminstall-g@vue/cli创建项目vuecreate项目名二、目录介绍src--main.js入口文件main.js介绍//引入vueimportVuefrom'vue'/......
  • 《态度-吴军》 读书笔记
    教育首先是关怀备至地、深思熟虑地、小心翼翼地去触及年轻的心灵。——苏霍姆林斯基一、前言《态度》一书为吴军老师给上高中和大学的两个女儿写的四十封家书,针对成长过......
  • linux工具grep的使用心得笔记
    grep作为linux管理中常用的三大工具之一(grep、awk、sed),其功能十分强大,因此难以对其进行全面的使用介绍,因此本文只作为个人学习的笔记之用。 grep的用处:在文本中匹配要......
  • 数论学习笔记
    逆元定义存在$a\timesx\equiv1\pmod{m}$,我们称\(x\)为\(a\)的逆元。完全剩余系假设\(1\toP-1\)都存在逆元,我们则称他为一个完全剩余系。当......
  • Spring 学习笔记
    Spring笔记1.Spring基本概念这里介绍一下,spring的基本概念,简单的说一下ioc是什么,ioc容器是什么,ioc怎么实现的。spring默认是单例的1.1IOCioc是一种思想叫控制反转......
  • MySql学习笔记--进阶05
          ......