当前位置:   article > 正文

【算法学习】线段树基础版

【算法学习】线段树基础版

一 线段树

1.概念

线段树可以理解为一个二叉树,如果是利用线段树求区间的和,那么每个结点的权值维护的是结点所维护区间的和,再将该区间一分为二,分别交由左右儿子维护。

拿区间1 - 4的和来举例子,

根结点维护的是区间1 到 4,结点权值是该区间的和,再将区间一份为二,其左儿子维护的是 1 - 2,右儿子维护的是 3 - 4 ,以此类推,直到结点维护的区间长度为1。

不难看出,每个节点的权值等于左右儿子的权值之和。

2.基本步骤

1.构造线段树

清楚了线段树的概念后,很容易构造线段树,一般算法题都是通过数组模拟二叉树,本文也采用这种方式。

  1. struct Tree {
  2. int l, r, weight;
  3. } tree[N];
  4. using namespace std;
  5. void build_tree(int i, int l, int r) {
  6. if (l == r) {
  7. tree[i] = {l, r, w[l]};
  8. return;
  9. }
  10. int mid = l + r >> 1;
  11. //构建左子树
  12. build_tree(i<<1, l, mid);
  13. //构建右子树
  14. build_tree(i<<1|1, mid + 1, r);
  15. //节点权值
  16. int sum = tree[i * 2].weight + tree[i * 2 + 1].weight;
  17. //更新区间
  18. tree[i] = {l,r,sum};
  19. }
2.区间查询

从根节点开始找目标区间

1.结点维护的区间是目标区间的子集,直接返回结点权值

2.左子树与目标区间有交集,递归左子树

2.右子树与目标区间有交集,递归右子树

4.返回sum

拿查找2-3区间和举例

从根节点开始,目标区间和左子树有交集,递归左子树,目标区间和右子树有交集 ,递归右子树;

接着看根节点左儿子,目标区间只和右子树有交集,递归右子树,

看根节点右儿子,目标区间只和左子树有交集,递归左子树, 

再向下递归发现,节点维护区间刚好是目标区间的子集,直接返回权值,结束向下递归。

代码

  1. int query(int i,int l,int r){
  2. //结点维护区间是目标区间的子集,直接返回权值
  3. if(tree[i].l>=l&&tree[i].r<=r)return tree[i].weight;
  4. int mid = tree[i].l + tree[i].r >>1;
  5. int sum = 0;
  6. if(l<=mid) sum += query(i*2,l,r);
  7. if(r>mid)sum+= query(i*2+1,l,r);
  8. return sum;
  9. }

3.区间修改 

从根节点开始找目标区间

1.结点维护的区间是目标区间的子集,修改权值,返回

2.左子树与目标区间有交集,递归左子树

2.右子树与目标区间有交集,递归右子树

4.更新结点权值(pushup)

拿给2-3区间加1和举例

从根节点开始,目标区间和左子树有交集,递归左子树,目标区间和右子树有交集 ,递归右子树;

接着看根节点左儿子,目标区间只和右子树有交集,递归右子树,

看根节点右儿子,目标区间只和左子树有交集,递归左子树, 

再向下递归发现,节点维护区间刚好是目标区间的子集,直接修改权值,结束向下递归。

代码

  1. void pushup(int i){
  2. tree[i].weight = tree[i<<1].weight + tree[i<<1|1].weight;
  3. }
  4. void modify(int i, int l, int r,int v)
  5. {
  6. if(tree[i].l>=l&&tree[i].r<=r) tree[i].weight+=v*(tree[i].r - tree[i].l+1);
  7. else{
  8. int mid = tree[i].l + tree[i].r >>1;
  9. if(l<=mid) modify(i<<1,l,r,v);
  10. if(r>mid) modify(i<<1|1,l,r,v);
  11. pushup(i);
  12. }
  13. }

例题

1.动态求连续区间和

给定 n 个数组成的一个数列,规定有两种操作,一是修改某个元素,二是求子数列[a,b]的连续和。

输入格式

第一行包含两个整数 n 和 m,分别表示数的个数和操作次数。

第二行包含n个整数,表示完整数列。

接下来 m 行,每行包含三个整数k, a, b(k = 0,表示求子数列[a, b]的和;k = 1,表示第 a 个数加b)。

数列从1开始计数。

输出格式

输出若干行数字,表示k=0 时,对应的子数列[a, b]的连续和。

数据范围

1≤n≤100000,

1≤m≤100000,

1≤a≤b≤n,

数据保证在任何时候,数列中所有元素之和均在 int 范围内。

输入样例:

10 5
1 2 3 4 5 6 7 8 9 10
1 1 5
0 1 3
0 4 8
1 7 5
0 4 8
1
2
3
4
5
6
7
输出样例:

11
30
35
1
2
3

ans

  1. #include <cstdio>
  2. #include <iostream>
  3. #include <algorithm>
  4. const int N = 1e5 + 10;
  5. struct segment_tree {
  6. int l, r, weight;
  7. } tree[N*4];
  8. int n,m;
  9. int w[N];
  10. using namespace std;
  11. void build_tree(int i, int l, int r) {
  12. if (l == r) {
  13. tree[i] = {l, r, w[l]};
  14. return;
  15. }
  16. int mid = l + r >> 1;
  17. //构建左子树
  18. build_tree(i<<1, l, mid);
  19. //构建右子树
  20. build_tree(i<<1|1, mid + 1, r);
  21. //节点权值
  22. int sum = tree[i * 2].weight + tree[i * 2 + 1].weight;
  23. //更新区间
  24. tree[i] = {l,r,sum};
  25. }
  26. void pushup(int i){
  27. tree[i].weight = tree[i<<1].weight + tree[i<<1|1].weight;
  28. }
  29. int query(int i,int l,int r){
  30. //结点维护区间是目标区间的子集,直接返回权值
  31. if(tree[i].l>=l&&tree[i].r<=r)return tree[i].weight;
  32. int mid = tree[i].l + tree[i].r >>1;
  33. int sum = 0;
  34. if(l<=mid) sum += query(i<<1,l,r);
  35. if(r>mid)sum+= query(i<<1|1,l,r);
  36. return sum;
  37. }
  38. void modify(int i, int l, int r,int v)
  39. {
  40. if(tree[i].l>=l&&tree[i].r<=r) tree[i].weight+=v*(tree[i].r - tree[i].l+1);
  41. else{
  42. int mid = tree[i].l + tree[i].r >>1;
  43. if(l<=mid) modify(i<<1,l,r,v);
  44. if(r>mid) modify(i<<1|1,l,r,v);
  45. pushup(i);
  46. }
  47. }
  48. int main()
  49. {
  50. scanf("%d%d", &n, &m);
  51. for (int i = 1; i <= n; i ++ ) scanf("%d", &w[i]);
  52. build_tree(1, 1, n);
  53. int k, a, b;
  54. while (m -- )
  55. {
  56. scanf("%d%d%d", &k, &a, &b);
  57. if (k == 0) printf("%d\n", query(1, a, b));
  58. else modify(1, a,a, b);
  59. }
  60. return 0;
  61. }

感谢你的阅读,希望本文对你有所帮助。 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/478021
推荐阅读
相关标签
  

闽ICP备14008679号