• 线段树的区间修改


    线段树2多加了个操作,pushdown就是用父节点的信息来下发到子节点进行更新,也即懒标记,用来区间修改

    线段树的所有操作

    pushup:子节点信息更新父节点信息

    pushdown:父节点信息更新子节点信息

    built:用堆的方式建立线段树

    modify:修改一个值/一个区间的信息

    query:询问一个区间的信息

    pushup放在更新之后,pushdown放在更新之前

    目录

    1.一个简单的整数问题2

    2.亚特兰蒂斯

    3.维护序列


    1.一个简单的整数问题2

    243. 一个简单的整数问题2 - AcWing题库

     这题之前用树状数组也可以做,现在用线段树来做一下

    1. #include
    2. using namespace std;
    3. typedef long long ll;
    4. const int N=1e5+10;
    5. int w[N];
    6. int n,m;
    7. struct Node
    8. {
    9. int l,r;
    10. ll sum,add;
    11. }tr[4*N];
    12. void pushup(int u)//用子节点信息更新父节点信息
    13. {
    14. tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;
    15. }
    16. void pushdown(int u)//用父节点信息更新子节点信息
    17. {
    18. auto &root=tr[u],&left=tr[u<<1],&right=tr[u<<1|1];
    19. if(root.add)//假如有的加
    20. {
    21. left.add+=root.add,left.sum+=(ll)(left.r-left.l+1)*root.add;//左节点加上
    22. right.add+=root.add,right.sum+=(ll)(right.r-right.l+1)*root.add;//右节点加上
    23. root.add=0;//根节点重新赋值0
    24. }
    25. }
    26. void built(int u,int l,int r)//建立线段树
    27. {
    28. if(l==r) tr[u]={l,r,w[l],0};//这个点的和等于w[l],add为0
    29. else
    30. {
    31. tr[u]={l,r,0,0};
    32. int mid=l+r>>1;
    33. built(u<<1,l,mid),built(u<<1|1,mid+1,r);//继续建左右子树
    34. pushup(u);//更新一下父节点信息
    35. }
    36. }
    37. void modify(int u,int l,int r,int d)//修改操作
    38. {
    39. if(l<=tr[u].l&&r>=tr[u].r)//假如这个u区间已经在l~r里了,则直接修改
    40. {
    41. tr[u].sum+=(ll)(tr[u].r-tr[u].l+1)*d;//区间每个数都加上d
    42. tr[u].add+=d;//这个add+=d
    43. }
    44. else
    45. {
    46. pushdown(u);//先把父节点的信息更新到子节点中去,在修改
    47. int mid=tr[u].l+tr[u].r>>1;
    48. if(l<=mid) modify(u<<1,l,r,d);//假如这个值在左区间,则递归左区间进行查找这个值进行修改
    49. if(r>mid) modify(u<<1|1,l,r,d);//假如这个值在右区间,则递归右区间进行查找这个值进行修改
    50. pushup(u);//修改完后更新父节点信息
    51. }
    52. }
    53. ll query(int u,int l,int r)//询问操作
    54. {
    55. if(tr[u].l>=l&&r>=tr[u].r) return tr[u].sum;//假如这个区间在l~r中了,则直接返回
    56. pushdown(u);//先把父节点的信息更新给子节点里
    57. ll ans=0;
    58. int mid=tr[u].l+tr[u].r>>1;
    59. if(l<=mid) ans=query(u<<1,l,r);//假如在左区间
    60. if(r>mid) ans+=query(u<<1|1,l,r);//假如在右区间,则加上
    61. return ans;//返回结果
    62. }
    63. int main()
    64. {
    65. scanf("%d%d",&n,&m);
    66. for(int i=1;i<=n;i++) scanf("%d",&w[i]);
    67. built(1,1,n);//建立线段树
    68. char op[2];
    69. while(m--)
    70. {
    71. int l,r,d;
    72. scanf("%s%d%d",op,&l,&r);
    73. if(*op=='Q') printf("%lld\n",query(1,l,r));//询问则直接输出
    74. else
    75. {
    76. scanf("%d",&d);
    77. modify(1,l,r,d);//修改
    78. }
    79. }
    80. return 0;
    81. }

    2.亚特兰蒂斯

    247. 亚特兰蒂斯 - AcWing题库

    这题拓展性不大,自己斟酌着看 ,比赛难想

    微分思想,把x进行正方形切分,然后y值进行线段树进行求长度,每个长方形的面积S=(xi-xi-1)*len,然后把所有的面积加起来则为总面积

    假如一个矩形,我们让其左边为+1,右边为-1

     然后用线段树进行两个操作

    1.将某个区间【l,r】+k

    2.整个区间长度大于0的区间总长为多少?也即len

    线段树要存的节点信息

    cnt:当前区间整个被覆盖的个数

    len:不考虑祖宗节点cnt的前提下,cnt>0的区间总长度

    1. #include
    2. using namespace std;
    3. const int N=1e4+10;
    4. int n;
    5. struct Segment//用来存横坐标
    6. {
    7. double x,y1,y2;
    8. int k;
    9. bool operator< (const Segment &t)const//横坐标按照从小到大排序
    10. {
    11. return x
    12. }
    13. }seg[N*2];
    14. struct Node//存y,也即线段树
    15. {
    16. int l,r;
    17. int cnt;//记录当前区间有多少个
    18. double len;//当前区间y的长度
    19. }tr[8*N];
    20. vector<double> ys;//用来离散化,把y值离散化到1~2*n中
    21. void pushup(int u)//用子节点信息更新父节点信息
    22. {
    23. if(tr[u].cnt) tr[u].len=ys[tr[u].r+1]-ys[tr[u].l];//假如当前区间被覆盖了,则加上对应的y的长度
    24. else if(tr[u].l!=tr[u].r) tr[u].len=tr[u<<1].len+tr[u<<1|1].len;//假如没有覆盖并且不是根节点,则长度等于两个子节点的长度和
    25. else tr[u].len=0;//假如是根节点则没有长度
    26. }
    27. void built(int u,int l,int r)//建立线段树
    28. {
    29. tr[u]={l,r,0,0};//刚开始没操作的长度与覆盖数都为0
    30. if(l!=r)
    31. {
    32. int mid=l+r>>1;
    33. built(u<<1,l,mid),built(u<<1|1,mid+1,r);//建立左右区间
    34. //pushup(u)这里不用也行,因为刚开始没有数的时候就是0
    35. }
    36. }
    37. void modify(int u,int l,int r,int k)//修改某个区间的值
    38. {
    39. if(tr[u].l>=l&&tr[u].r<=r)//假如当前区间在l~r中
    40. {
    41. tr[u].cnt+=k;//则直接修改
    42. pushup(u);//把修改传到子节点中
    43. }
    44. else
    45. {
    46. int mid=tr[u].l+tr[u].r>>1;
    47. if(l<=mid) modify(u<<1,l,r,k);//假如在左边有,则递归左边
    48. if(r>mid) modify(u<<1|1,l,r,k);//假如在右边有,则递归右边
    49. pushup(u);
    50. }
    51. }
    52. int find(double y)//离散化,把y离散到坐标里
    53. {
    54. return lower_bound(ys.begin(),ys.end(),y)-ys.begin();//用二分找出y的位置
    55. }
    56. int main()
    57. {
    58. int T=1;
    59. while(scanf("%d",&n),n)
    60. {
    61. ys.clear();//清空上一层的状态
    62. double x1,x2,y1,y2;
    63. for(int i=1,j=0;i<=n;i++)
    64. {
    65. scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
    66. seg[j++]={x1,y1,y2,1},seg[j++]={x2,y1,y2,-1};//左边则+1,右边则-1,加到存x中的结构体中去
    67. ys.push_back(y1),ys.push_back(y2);//把y也存进离散化里头
    68. }
    69. //将y去重
    70. sort(ys.begin(),ys.end());
    71. ys.erase(unique(ys.begin(),ys.end()),ys.end());
    72. built(1,0,ys.size()-2);//建立一颗线段树
    73. sort(seg,seg+2*n);//将x从小到大排序
    74. double res=0;
    75. for(int i=0;i<2*n;i++)
    76. {
    77. if(i) res+=tr[1].len*(seg[i].x-seg[i-1].x);//当i>0才能计算
    78. modify(1,find(seg[i].y1),find(seg[i].y2)-1,seg[i].k);//修改线段树y中的值
    79. }
    80. printf("Test case #%d\n",T++);
    81. printf("Total explored area: %.2lf\n\n",res);
    82. }
    83. return 0;
    84. }

    3.维护序列

    信息学奥赛一本通(C++版)在线评测系统 (ssoier.cn)

    第一题的进阶版

    需要记录两个pushdown的值,也即add与mul

     

    1. #include
    2. using namespace std;
    3. typedef long long ll;
    4. const int N=1e5+10;
    5. int w[N];
    6. int n,m,p;
    7. struct Node//线段树
    8. {
    9. int l,r;
    10. int sum,add,mul;
    11. }tr[4*N];
    12. void pushup(int u)//子节点更新父节点
    13. {
    14. tr[u].sum=(tr[u<<1].sum+tr[u<<1|1].sum)%p;
    15. }
    16. void eval(Node &t,int add,int mul)//用来更新某个节点的值
    17. {
    18. t.sum=((ll)t.sum*mul+(ll)(t.r-t.l+1)*add)%p;//总和等于原来的和*mul+数的多少*add
    19. t.mul=(ll)t.mul*mul%p;//新的mul等于原来的mul*mul
    20. t.add=((ll)t.add*mul+add)%p;//新的add=原来的add*mul+add
    21. }
    22. void pushdown(int u)//用父节点信息更新子节点信息
    23. {
    24. eval(tr[u<<1],tr[u].add,tr[u].mul);//更新左节点信息
    25. eval(tr[u<<1|1],tr[u].add,tr[u].mul);//更新右节点信息
    26. tr[u].add=0,tr[u].mul=1;//清空
    27. }
    28. void built(int u,int l,int r)//用来建立线段树
    29. {
    30. if(l==r) tr[u]={l,r,w[l],0,1};
    31. else
    32. {
    33. tr[u]={l,r,0,0,1};
    34. int mid=l+r>>1;
    35. built(u<<1,l,mid),built(u<<1|1,mid+1,r);//建立左右子树
    36. pushup(u);//更新一遍父节点信息
    37. }
    38. }
    39. void modify(int u,int l,int r,int add,int mul)//修改操作
    40. {
    41. if(tr[u].l>=l&&tr[u].r<=r) eval(tr[u],add,mul);//假如这个区间在l~r之间直接更新
    42. else
    43. {
    44. pushdown(u);//把之前的父节点信息更新到子节点信息中去
    45. int mid=tr[u].l+tr[u].r>>1;
    46. if(l<=mid) modify(u<<1,l,r,add,mul);//假如左边有这部分区间,则递归更新
    47. if(r>mid) modify(u<<1|1,l,r,add,mul);//假如右边有这部分区间,则递归更新
    48. pushup(u);//更新一遍父节点信息
    49. }
    50. }
    51. int query(int u,int l,int r)//询问操作
    52. {
    53. if(tr[u].l>=l&&tr[u].r<=r) return tr[u].sum;//假如这个区间在l~r之间则直接返回sum
    54. pushdown(u);//把父节点信息传递到子节点中去
    55. int mid=tr[u].l+tr[u].r>>1;
    56. int res=0;
    57. if(l<=mid) res=query(u<<1,l,r);//假如左边有这个区间
    58. if(r>mid) res=(res+query(u<<1|1,l,r))%p;//假如右边也有这个区间则加上
    59. return res;//返回答案
    60. }
    61. int main()
    62. {
    63. scanf("%d%d",&n,&p);
    64. for(int i=1;i<=n;i++) scanf("%d",&w[i]);
    65. built(1,1,n);//建立线段树
    66. scanf("%d",&m);
    67. int op,l,r,d;
    68. while(m--)
    69. {
    70. scanf("%d%d%d",&op,&l,&r);
    71. if(op==1)
    72. {
    73. scanf("%d",&d);
    74. modify(1,l,r,0,d);//乘d相当于加0乘d
    75. }
    76. else if(op==2)
    77. {
    78. scanf("%d",&d);
    79. modify(1,l,r,d,1);//加d相当于加d乘1
    80. }
    81. else printf("%d\n",query(1,l,r));//输出询问
    82. }
    83. return 0;
    84. }

     

  • 相关阅读:
    GAN网络系列博客(二):改善StyleGAN的图像质量
    总结数据结构-1
    vxe-table表格校验失败后保持可以编辑状态
    大厂面试题-JVM为什么使用元空间替换了永久代?
    Neo4j数据库删除数据
    sparksession对象简介
    Chrome插件精选 — 屏幕录像插件
    Prometheus 使用cadvisor采集docker容器监控数据
    STM32物联网项目-高级定时器软件仿真输出互补PWM信号
    Python实现自动生成日报数据,又掌握一个有用的技能
  • 原文地址:https://blog.csdn.net/m0_63729880/article/details/127093901