难以平复鸡冻的心情,虽然可能在大佬眼里这是水题,但对蒟蒻的我来说这是个巨大的突破(谢谢我最亲爱的lp陪我写完,给我力量)。网上关于线段树的题解都很玄学,包括李煜东的《算法竞赛进阶指南》中的相关内容一样,不能给我一眼看上去就明白的清晰的思路。请允许我作为用了10个小时做出这道题的“过来人”清晰的提一下比较难想的几个点。首先我根据书上和其它博客上的大致思路,选择了结构体来实现,其实用数组也是可以的,但我感觉更加的清晰。

第一个难点,书上的图给的样例就是1....n。我们的结点表示的区间同样也是类似的,那么我这样的蒟蒻就会产生误解,其实就是根据读入的编号,这点是如何发现的呢?我们可以根据洛谷上面的题面知道,对连续区间的修改,不难想到表示的区间就是读入时的编号。

第二个难点,如何建树,这个书上给的解释很清楚,按照书上的来就不会有问题。但是有的博客上的和书上不一样,它没有用下文的shu[x].l,shu[x].r来表示对应的x结点所代表的区间,其实这也是可以的,起码对于我的程序来说,因为你寻找区间的时候用的递归是可以承载这个信息的。

第三个难点,如何给区间中的每一个数加值,只要明白了这一点,其实后面的寻找区间中的和也就是我的find_tree和add_tree中的操作过程是大致相同的。关键是看边界条件,我会在代码上标注出来。

第四个难点,也就是懒惰标记,这是线段树优点的关键所在,就是记录下在何时你的区间正好可以加(范围只能小,不能超),关键看操作。

ps:洛谷中的运势是大吉,诸事皆宜,看起来有几分道理。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. struct node{
  4. long long l,r,dat;
  5. }shu[];//结构体,l代表区间最左,r代表区间最右,dat是区间中数的和。
  6. long long a[];//读入和后面建树要用
  7. long long lazy[];//懒惰标记
  8. long long b,c,d,ans;
  9.  
  10. void build_tree(long long x,long long y,long long z){//建树
  11. shu[x].l=y;shu[x].r=z;
  12. if (y==z) {shu[x].dat=a[y];return;}//如果区间大小是1则代表是叶结点,不用再向下建树
  13. else {
  14. long long m=(z+y) >> ;//中点
  15. build_tree(x*,y,m);//最子树的结点是2*x(根据完全二叉树来做的,这个就是用数组模拟,说建数就是装逼的说法,本质相同,只是在你的思维里代表了另一种结构。)
  16. build_tree(x*+,m+,z);
  17. shu[x].dat=shu[x*].dat+shu[x*+].dat;//区间和等于左子区间和+右子区间和。
  18. }
  19. }
  20.  
  21. void add_tree(long long x,long long add){//区间加值
  22. shu[x].dat+=add;//加上区间中要加的值
  23. if (b<=shu[x].l&&shu[x].r<=c) {//如果区间在要加的区间之中就用懒惰标记记录它的子区间,因为它已经被加了
  24. if (shu[x].l!=shu[x].r){
  25. lazy[x*]+=d;
  26. lazy[x*+]+=d;
  27. }
  28. return;
  29. }
  30. else {
  31. long long mid=(shu[x].l+shu[x].r)/;//4个边界的判断,读者可以自己去想一下,是本题的难点。
  32. if (mid>=b&&mid<=c) {
  33. if (shu[x].l>=b) add_tree(x*,(mid-shu[x].l+)*d);
  34. else add_tree(x*,(mid-b+)*d);
  35. }
  36. if (mid+<=c&&mid+>=b) {
  37. if (shu[x].r>=c) add_tree(x*+,(c-mid)*d);
  38. else add_tree(x*+,(shu[x].r-mid)*d);//我是用add这个局部变量来表示要加的值,用此区间中有多少是包括在要加区间中的再*加的数
  39. }
  40. if (mid>c){
  41. if (shu[x].l>=b&&shu[x].l<=c) add_tree(x*,(c-shu[x].l+)*d);
  42. else if (shu[x].l<b)add_tree(x*,(c-b+)*d);
  43. }
  44. if (mid+<b){
  45. if (shu[x].r<=c&&shu[x].r>=b) add_tree(x*+,(shu[x].r-b+)*d);
  46. else if (shu[x].r>c) add_tree(x*+,(c-b+)*d);
  47. }
  48. }
  49. }
  50.  
  51.  
  52. void find_tree(long long x){//查找区间和,和上面一个过程十分的相似。
  53. shu[x].dat+=(lazy[x]*(shu[x].r-shu[x].l+));
  54. if (b<=shu[x].l&&c>=shu[x].r){
  55. ans=ans+shu[x].dat;
  56. if (lazy[x]!=&&shu[x].l!=shu[x].r){
  57. lazy[x*]+=lazy[x];
  58. lazy[x*+]+=lazy[x];
  59. lazy[x]=;
  60. }
  61. if (shu[x].r==shu[x].l) lazy[x]=;
  62. return;
  63. }
  64. else {
  65. if (shu[x].r==shu[x].l) {lazy[x]=;return;}
  66. long long mid=(shu[x].l+shu[x].r)/;
  67. if (lazy[x]!=){
  68. lazy[x*]+=lazy[x];
  69. lazy[x*+]+=lazy[x];
  70. lazy[x]=;
  71. }
  72. if (mid>=b&&mid<=c) find_tree(x*);
  73. if ((mid+)<=c&&(mid+)>=b) find_tree((x*)+);
  74. if (mid>c&&shu[x].l<=c) find_tree(x*);
  75. if (mid+<b&&shu[x].r>=b) find_tree(x*+);
  76. }
  77. }
  78.  
  79.  
  80. int main(){
  81. long long n,m;
  82. cin>>n>>m;//读入
  83. memset(lazy,,sizeof(lazy));
  84. for (long long i=;i<=n;i++) cin>>a[i];
  85. build_tree(,,n);//建树
  86. for (long long i=;i<=m;i++){
  87. long long a;
  88. cin>>a;
  89. if (a==){
  90. cin>>b>>c>>d;
  91. add_tree(,(c-b+)*d);//加值
  92. }
  93. else {
  94. ans=;
  95. cin>>b>>c;
  96. find_tree();
  97. cout<<ans<<endl;//查询
  98. }
  99. }
  100. }

再次用了标准的模板格式进行了标准化的修改。下面是代码。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. struct node{
  4. long long l,r,dat;
  5. }shu[];
  6. long long lazy[];
  7. long long a[];
  8. long long ans,b,c,d,n,m;
  9. void build_tree(long long x,long long y,long long z){
  10. shu[x].l=y;shu[x].r=z;
  11. if (y==z){
  12. shu[x].dat=a[y];
  13. return;
  14. }
  15. long long mid=(y+z)/;
  16. build_tree(x*,y,mid);
  17. build_tree(x*+,mid+,z);
  18. shu[x].dat=shu[x*].dat+shu[x*+].dat;
  19. }
  20. void add_tree(long long x){
  21. if (shu[x].l>=b&&shu[x].r<=c){
  22. shu[x].dat+=(d*(shu[x].r-shu[x].l+));
  23. if (shu[x].l!=shu[x].r){
  24. lazy[x*]+=d;
  25. lazy[x*+]+=d;
  26. }
  27. return;
  28. }
  29. long long mid=(shu[x].l+shu[x].r)/;
  30. if (b<=mid) add_tree(*x);
  31. if (c>mid) add_tree(*x+);
  32. shu[x].dat=shu[x*].dat+shu[x*+].dat+lazy[*x]*(shu[x*].r-shu[x*].l+)+lazy[*x+]*(shu[x*+].r-shu[x*+].l+);
  33. }
  34. void find_tree(long long x){
  35. if (lazy[x]!=)shu[x].dat+=lazy[x]*(shu[x].r-shu[x].l+);
  36. if (shu[x].l!=shu[x].r){
  37. lazy[x*]+=lazy[x];
  38. lazy[x*+]+=lazy[x];
  39. }
  40. lazy[x]=;
  41. if (shu[x].l>=b&&shu[x].r<=c){
  42. ans+=shu[x].dat;
  43. return;
  44. }
  45. long long mid=(shu[x].l+shu[x].r)/;
  46. if (b<=mid) find_tree(*x);
  47. if (c>mid) find_tree(*x+);
  48. }
  49. int main(){
  50. cin>>n>>m;
  51. for (long long i=;i<=n;i++) cin>>a[i];
  52. build_tree(,,n);
  53. memset(lazy,,sizeof(lazy));
  54. for (long long i=;i<=m;i++){
  55. long long a;
  56. cin>>a;
  57. if (a==){
  58. cin>>b>>c>>d;
  59. add_tree();
  60. }
  61. else if (a==){
  62. cin>>b>>c;
  63. ans=;
  64. find_tree();
  65. cout<<ans<<endl;
  66. }
  67. }
  68. }

可以看出来,更加的简洁了。(书上的解法真标准)但是思路和我之前的程序是大相径庭的,可以仔细研究一下两者的区别。

洛谷P3372线段树1的更多相关文章

  1. 洛谷P3372线段树模板1——线段树

    题目:https://www.luogu.org/problemnew/show/P3372 线段树模板. 代码如下: #include<iostream> #include<cst ...

  2. 洛谷 P3372 线段树1

    这是一道模板题 线段树介绍https://www.cnblogs.com/nvwang123/p/10420832.html #include<bits/stdc++.h> using n ...

  3. NOIP2017提高组Day2T3 列队 洛谷P3960 线段树

    原文链接https://www.cnblogs.com/zhouzhendong/p/9265380.html 题目传送门 - 洛谷P3960 题目传送门 - LOJ#2319 题目传送门 - Vij ...

  4. 洛谷P3373线段树模板2

    题目:https://www.luogu.org/problemnew/show/P3373 带乘的线段树,更新时把加的标记也乘一下,然后取值时先乘后加. 代码如下: #include<iost ...

  5. 洛谷P3373 线段树2(补上注释了)

    毒瘤题.找了一下午+晚上的BUG,才发现原来query_tree写的是a%p; 真的是一个教训 UPD:2019.6.18 #include<iostream> #include<c ...

  6. 洛谷3372线段树模板题 对区间+k或者查询区间和

    #include<bits/stdc++.h> using namespace std; typedef unsigned int ui; typedef long long ll; ty ...

  7. 洛谷P3372/poj3468(线段树lazy_tag)(询问区间和,支持区间修改)

    洛谷P3372 //线段树 询问区间和,支持区间修改 #include <cstdio> using namespace std; struct treetype { int l,r; l ...

  8. 洛谷1087 FBI树 解题报告

    洛谷1087 FBI树 本题地址:http://www.luogu.org/problem/show?pid=1087 题目描述 我们可以把由“0”和“1”组成的字符串分为三类:全“0”串称为B串,全 ...

  9. 洛谷P3018 [USACO11MAR]树装饰Tree Decoration

    洛谷P3018 [USACO11MAR]树装饰Tree Decoration树形DP 因为要求最小,我们就贪心地用每个子树中的最小cost来支付就行了 #include <bits/stdc++ ...

随机推荐

  1. Zookeeper入门看这篇就够了!!

    Zookeeper是什么 官方文档上这么解释zookeeper,它是一个分布式服务框架,是Apache Hadoop 的一个子项目,它主要是用来解决分布式应用中经常遇到的一些数据管理问题,如:统一命名 ...

  2. SVN Trunk Tag Branch

    http://blog.csdn.net/vbirdbest/article/details/51122637

  3. C# dynamic与var的区别

    1.C#编程总结(十四)dynamic 2.var和dynamic的区别及如何正确使用dynamic?

  4. NET Core 2.1.0 now available

    ASP.NET Core 2.1.0 now available https://blogs.msdn.microsoft.com/webdev/2018/05/30/asp-net-core-2-1 ...

  5. Spring创建对象的几种方法

    一.通过构造器 无参构造器 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns=" ...

  6. Spring Cloud 熔断器

    目录 Spring Cloud 熔断器 Hystrix ribbon中使用hystrix feign中使用hystrix Spring Cloud 熔断器 在微服务架构中,根据业务来拆分成一个个的服务 ...

  7. 死磕 java并发包之LongAdder源码分析

    问题 (1)java8中为什么要新增LongAdder? (2)LongAdder的实现方式? (3)LongAdder与AtomicLong的对比? 简介 LongAdder是java8中新增的原子 ...

  8. DOM操作(二)对元素的操作(创建,追加,删除)

    1 创建新的 HTML 元素 (节点) var divDom=document.createElement('div'); 2 添加新元素到尾部 element.appendChild(para); ...

  9. Vmware 虚拟硬盘 合并多个分割文件

    有时,当我们创建虚拟机vmware里面的应用程序,我们可能会选择创建分割的虚拟磁盘中的多个文件2 GB的文件,这是为了提高复制过程,主要用于存储虚拟机文件系统不支持创建更大的文件. 如果我们需要将它转 ...

  10. 四道java语言练习基础题:

    一.==符的使 首先看一段比较有意思的代码 Integer a = 1000,b=1000; Integer c = 100,d=100; public void mRun(final String ...