• 线段树模板


    好文分享:【数据结构】线段树(Segment Tree) - 小仙女本仙 - 博客园

    线段树和树状数组的基本功能都是在某一满足结合律的操作(比如加法,乘法,最大值,最小值)下,O(logn)的时间复杂度内修改单个元素并且维护区间信息。

    不同的是,树状数组只能维护前缀“操作和”(前缀和,前缀积,前缀最大最小),而线段树可以维护区间操作和。

    线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。而未优化的空间复杂度为2N,实际应用时一般还要开4N4N的数组以免越界,因此有时需要离散化让空间压缩。

    线段树操作:1.单点修改       

    只要不断修改这个区间及其父区间即可,而不会影响其他区间情况,时间复杂度O(logn)

    2.区间查询(最小值,最小值出现次数等某些)

    比如要查询【2,5】这个区间,我们看看能不能将这个区间拆成线段树上的若干段区间。从根节点开始看,【2,5】与【1,7】没有什么关系,所以往下看,查询【2,5】与【1,4】的交【2,4】,查询【2,5】与【5,7】的交【5,5】,然后再继续往下递归,直到有满足的区间或者有孤立的节点了,然后再不断返回即可

    例题:P3372 【模板】线段树 1

    本题要进行区间加 和 区间查询操作, 其实这个树状数组改一改也能做,开两个树状数组就行了,这里采用线段树操作, 直接上板子。

    1. #include
    2. using namespace std;
    3. #define ll long long
    4. typedef pair<int, int> PII;
    5. #define pb push_back
    6. const int N = 2E5 + 5;
    7. int n, q, a[N];
    8. const int mod = 1e9 + 7;
    9. struct tag {
    10. ll mul, add;
    11. };
    12. tag operator + (const tag &t1, const tag &t2) {
    13. // (mul1 + add1) * mul2 + add2;
    14. return {t1.mul * t2.mul , (t1.add * t2.mul % mod + t2.add)};
    15. }
    16. struct node {
    17. tag t;
    18. ll val;
    19. int sz;
    20. }seg[N * 4];
    21. void update(int id) {
    22. seg[id].val = (seg[id * 2].val + seg[id * 2 + 1].val);
    23. }
    24. void build(int id, int l, int r) {
    25. seg[id].t = (tag){1, 0};
    26. seg[id].sz = r - l + 1;
    27. if(l == r){
    28. seg[id].val = a[l];
    29. } else {
    30. int mid = (l + r) / 2;
    31. build(id * 2, l, mid);
    32. build(id * 2 + 1, mid + 1, r);
    33. update(id);
    34. }
    35. }
    36. void settag(int id, tag t) {
    37. seg[id].val = seg[id].val * t.mul + seg[id].sz * t.add;
    38. seg[id].t = seg[id].t + t;
    39. }
    40. void pushdown(int id) {
    41. if(seg[id].t.mul != 1 || seg[id].t.add != 0) {
    42. settag(id * 2, seg[id].t);
    43. settag(id * 2 + 1, seg[id].t);
    44. seg[id].t.mul = 1;
    45. seg[id].t.add = 0;
    46. }
    47. }
    48. void modify(int id, int l, int r, int ql, int qr, tag t) {
    49. if(l == ql && r == qr) {
    50. settag(id, t);
    51. return;
    52. }
    53. pushdown(id);
    54. int mid = (l + r) / 2;
    55. if(qr <= mid) modify(id * 2, l, mid, ql, qr, t);
    56. else if(ql > mid) modify(id * 2 + 1, mid + 1, r, ql, qr, t);
    57. else modify(id * 2, l, mid, ql, mid, t),
    58. modify(id * 2 + 1, mid + 1, r, mid + 1, qr, t);
    59. update(id);
    60. }
    61. ll query(int id, int l, int r, int ql, int qr) {
    62. if(l == ql && r == qr) {
    63. return seg[id].val;
    64. }
    65. pushdown(id);
    66. int mid = (l + r) / 2;
    67. if(qr <= mid) return query(id * 2, l, mid, ql, qr);
    68. else if(ql > mid) return query(id * 2 + 1, mid + 1, r, ql, qr);
    69. else return (query(id * 2, l, mid, ql, mid) +
    70. query(id * 2 + 1, mid + 1, r, mid + 1, qr));
    71. }
    72. int main(){
    73. scanf("%d %d", &n, &q);
    74. for(int i = 1; i <= n; ++i)
    75. scanf("%d", &a[i]);
    76. build(1, 1, n);
    77. while(q--) {
    78. int ty; scanf("%d", &ty);
    79. if(ty == 1) {
    80. int l, r, d;
    81. scanf("%d %d %d", &l, &r, &d);
    82. modify(1, 1, n, l, r, (tag){1, d});
    83. } else {
    84. int l, r; scanf("%d %d", &l, &r);
    85. printf("%lld\n", query(1, 1, n, l, r));
    86. }
    87. }
    88. return 0;
    89. }

    例题2:P3373 【模板】线段树 2

    这里看到需要好多操作, 我们肯定要利用标记去做这些操作, 我们可以让标记记录 + 和 * 两种操作, 然后将所有的修改变成这俩个操作。 如:

    • 将某区间每一个数乘上 x,那么就是*x + 0,

    • 将某区间每一个数加上 x,  那么就是*0 + x;

    • 求出某区间每一个数的和,  直接无脑query

      1. #include
      2. using namespace std;
      3. #define ll long long
      4. typedef pair<int, int> PII;
      5. #define pb push_back
      6. const int N = 2E5 + 5;
      7. int n, q, a[N];
      8. const int mod = 1e9 + 7;
      9. struct tag {
      10. ll mul, add;
      11. };
      12. tag operator + (const tag &t1, const tag &t2) {
      13. // (mul1 + add1) * mul2 + add2;
      14. return {t1.mul * t2.mul, (t1.add * t2.mul + t2.add)};
      15. }
      16. struct node {
      17. tag t;
      18. ll val;
      19. int sz;
      20. }seg[N * 4];
      21. void update(int id) {
      22. seg[id].val = (seg[id * 2].val + seg[id * 2 + 1].val);
      23. }
      24. void build(int id, int l, int r) {
      25. seg[id].t = (tag){1, 0};
      26. seg[id].sz = r - l + 1;
      27. if(l == r){
      28. seg[id].val = a[l];
      29. } else {
      30. int mid = (l + r) / 2;
      31. build(id * 2, l, mid);
      32. build(id * 2 + 1, mid + 1, r);
      33. update(id);
      34. }
      35. }
      36. void settag(int id, tag t) {
      37. seg[id].val = seg[id].val * t.mul + seg[id].sz * t.add;
      38. seg[id].t = seg[id].t + t;
      39. }
      40. void pushdown(int id) {
      41. if(seg[id].t.mul != 1 || seg[id].t.add != 0) {
      42. settag(id * 2, seg[id].t);
      43. settag(id * 2 + 1, seg[id].t);
      44. seg[id].t.mul = 1;
      45. seg[id].t.add = 0;
      46. }
      47. }
      48. void modify(int id, int l, int r, int ql, int qr, tag t) {
      49. if(l == ql && r == qr) {
      50. settag(id, t);
      51. return;
      52. }
      53. pushdown(id);
      54. int mid = (l + r) / 2;
      55. if(qr <= mid) modify(id * 2, l, mid, ql, qr, t);
      56. else if(ql > mid) modify(id * 2 + 1, mid + 1, r, ql, qr, t);
      57. else modify(id * 2, l, mid, ql, mid, t),
      58. modify(id * 2 + 1, mid + 1, r, mid + 1, qr, t);
      59. update(id);
      60. }
      61. ll query(int id, int l, int r, int ql, int qr) {
      62. if(l == ql && r == qr) {
      63. return seg[id].val;
      64. }
      65. pushdown(id);
      66. int mid = (l + r) / 2;
      67. if(qr <= mid) return query(id * 2, l, mid, ql, qr);
      68. else if(ql > mid) return query(id * 2 + 1, mid + 1, r, ql, qr);
      69. else return (query(id * 2, l, mid, ql, mid) +
      70. query(id * 2 + 1, mid + 1, r, mid + 1, qr));
      71. }
      72. int main(){
      73. scanf("%d %d", &n, &q);
      74. for(int i = 1; i <= n; ++i)
      75. scanf("%d", &a[i]);
      76. build(1, 1, n);
      77. while(q--) {
      78. int ty; scanf("%d", &ty);
      79. if(ty <= 3) {
      80. int l, r, d;
      81. scanf("%d %d %d", &l, &r, &d);
      82. if(ty == 1) modify(1, 1, n, l, r, (tag){1, d});
      83. else if (ty == 2) modify(1, 1, n, l, r, (tag){d, 0});
      84. else modify(1, 1, n, l, r, (tag){0, d});
      85. } else {
      86. int l, r; scanf("%d %d", &l, &r);
      87. printf("%lld\n", query(1, 1, n, l, r));
      88. }
      89. }
      90. return 0;
      91. }

  • 相关阅读:
    字节跳动的顶级架构师竟然整理出程序员向架构师的必备转型之路
    android桌面插件每秒刷新
    vue学习之vue cli创建项目
    【网络编程开发系列】好端端的MQTT-broker重新部署后居然出现TLS握手失败了
    问题求助 -MindSpore 训练问题
    [AutoSar NVM] 存储架构
    Pandas知识点-详解表级操作管道函数pipe
    Linux常用命令(下).
    Selenium-python环境安装谷歌驱动
    Fluent Support – 适用于 WordPress 的客户支持和帮助台插件
  • 原文地址:https://blog.csdn.net/m0_61837058/article/details/127580008