• 线段树——你能回答这些问题吗?(经典应用)


    传送门:245. 你能回答这些问题吗 - AcWing题库

    思路:

    1.在存储的结构体里面加一个sum用于记录当前节点区间的区间和,当前节点p的区间和就是两个子节点的区间和。

    t[p].sum=t[p*2].sum+t[2*p+1].sum;

    2.再用一个lmax和一个rmax记录当前区间内严格从pl开始的前缀区间和的最大值和严格从pr开始后      缀区间和的最大值。

          当前节点p的lmax取值有以下两种(rmax的情况与左子树的情况类似):

          一是左子树的lmax

         二是右子树的lmax加上左子树的区间和sum,因为这里lmax要求严格从当前区间的最左边开  始。

    1. t[p].lmax=max(t[2*p].lmax,t[2*p].sum+t[2*p+1].lmax);
    2. t[p].rmax=max(t[2*p+1].rmax,t[2*p+1].sum+t[2*p].rmax);

    3.用一个data记录当前节点所包含的区间的某一段连续区间和的最大值。

    想要求出当前节点p的区间[l,r]的某一段连续区间和最大值有三种情况:

    一是:左节点2*p的区间和最大值,也就是t[2*p].data

    二是:右节点2*p+1的区间和最大值,也就是t[2*p+1].data

    三是:lmax+rmax  ,当且仅当lmax和rmax都是非负数。拼接起来刚好是一段连续的区间。

    由上述三种情况得到下面的公式

    t[p].data=max({t[2*p].data,t[p*2+1].data,t[2*p].rmax+t[2*p+1].lmax});

    以上的三个部分都可以在建树和单点修改的过程中递归的进行更新。

    ------------------------------------------

    至于区间内最大连续区间和查询的方法与区间最大值的查询方法类似:

    细分为三类情况:

    第一种也是最简单的一种:l<=pl<=pr<=r直接返回t[p].data

    第二种:pl<=l<=pr<=r ,这种情况下,t[p].data的取值与l 和 mid 的关系有关(mid=(pl+pr)>>1)

    当l>=mid+1时可以直接进入右子树就能变成第一种情况。

    此时取值的可能只有一种就是右子树节点t[2*p+1].lmax

    当l<=mid时则需要递归左子树求出左子树的lmax以及 右子树直接返回值。

    此时取值可能有t[2*p].lmax 和t[2*p].sum+t[2*p+1].lmax

    l<=pl<=r<=pr的情况和上面类似,省略。

    第三种:pl<=l<=r<=pr,这种情况只要两个子树都分别递归进去就能变成第二种情况。

    代码:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. using namespace std;
    7. typedef long long ll;
    8. typedef pair<int,int> PII;
    9. const int N=1e6+10;
    10. int a[N];
    11. struct tree
    12. {
    13. int l,r;
    14. int data;
    15. int sum;
    16. int lmax;
    17. int rmax;
    18. }t[N*4];
    19. void bulid (int p,int l,int r) //线段树建树
    20. {
    21. t[p].l=l,t[p].r=r;
    22. if(l==r)
    23. {
    24. t[p].data=a[l];
    25. t[p].sum=a[l];
    26. t[p].lmax=a[l];
    27. t[p].rmax=a[l];
    28. return ;
    29. }
    30. int mid=(l+r)/2;
    31. bulid(2*p,l,mid);
    32. bulid(2*p+1,mid+1,r);
    33. t[p].sum=t[p*2].sum+t[2*p+1].sum;
    34. t[p].lmax=max(t[2*p].lmax,t[2*p].sum+t[2*p+1].lmax);
    35. t[p].rmax=max(t[2*p+1].rmax,t[2*p+1].sum+t[2*p].rmax);
    36. t[p].data=max({t[2*p].data,t[p*2+1].data,t[2*p].rmax+t[2*p+1].lmax});
    37. }
    38. //单点修改
    39. void change(int p,int x,int v)
    40. {
    41. if(t[p].l==t[p].r)
    42. {
    43. t[p].data=v;
    44. t[p].sum=v;
    45. t[p].lmax=v;
    46. t[p].rmax=v;
    47. return ;
    48. }
    49. int mid =(t[p].l+t[p].r)/2;
    50. if(x<=mid) change(p*2,x,v);
    51. else change(2*p+1,x,v);
    52. t[p].sum=t[p*2].sum+t[2*p+1].sum;
    53. t[p].lmax=max(t[2*p].lmax,t[2*p].sum+t[2*p+1].lmax);
    54. t[p].rmax=max(t[2*p+1].rmax,t[2*p+1].sum+t[2*p].rmax);
    55. t[p].data=max({t[2*p].data,t[p*2+1].data,t[2*p].rmax+t[2*p+1].lmax});
    56. }
    57. //区间查询:
    58. tree ask(int p,int l,int r)
    59. {
    60. if(l<=t[p].l&&r>=t[p].r)
    61. return t[p];
    62. int mid=(t[p].l+t[p].r)>>1,val=-(1<<30);
    63. tree a,b,c;
    64. a.data=a.sum=a.lmax=a.rmax=val;
    65. b.data=b.sum=b.lmax=b.rmax=val;
    66. c.sum=0;
    67. if(l<=mid)
    68. {
    69. a=ask(p*2,l,r);
    70. c.sum+=a.sum;
    71. }
    72. if(r>mid)
    73. {
    74. b=ask(2*p+1,l,r);
    75. c.sum+=b.sum;
    76. }
    77. c.data=max({a.data,b.data,a.rmax+b.lmax});
    78. c.lmax=max(a.lmax,b.lmax+a.sum);
    79. if(l>mid) //此处的作用是当上面的a没有被更新时是没有返回值的,c的数值会是负无穷,此时需要到右子树获得真正的返回值
    80. c.lmax=max(c.lmax,b.lmax);
    81. c.rmax=max(b.rmax,b.sum+a.rmax);
    82. if(r<=mid)
    83. c.rmax=max(c.rmax,a.rmax);
    84. return c;
    85. }
    86. int main()
    87. {
    88. int n,m;
    89. scanf("%d%d",&n,&m);
    90. for(int i=1;i<=n;i++)
    91. scanf("%d",&a[i]);
    92. bulid(1,1,n);
    93. while(m--)
    94. {
    95. int k,x,y;
    96. scanf("%d%d%d",&k,&x,&y);
    97. if(k==1)
    98. {
    99. if(x>y)
    100. swap(x,y);
    101. printf("%d\n",ask(1,x,y).data);
    102. }else
    103. {
    104. change(1,x,y);
    105. }
    106. }
    107. return 0;
    108. }

  • 相关阅读:
    2021年JAVA 精心整理的常见面试题-附详细答案【持续更新~~】
    JavaScript基础第02天—运算符(操作符)—流程控制—循环—代码规范
    LeetCode 349 两个数组的交集 - Java 实现
    特斯拉/小鹏「调整」软件策略,智能驾驶「标配」进入全新周期
    javamail——邮件发送
    【FAQ】【Push Kit】 华为怎么设置角标
    Opencv基本操作
    代理模式:静态代理与动态代理(JDK、CGLIB、javassist动态代理)
    Mybatis-Plus3.x的使用
    Flutter热更新技术探索
  • 原文地址:https://blog.csdn.net/m0_62327332/article/details/126913897