zoukankan      html  css  js  c++  java
  • 线段树--线段树的单点修改,区间修改,区间最值,区间求和

     

    题目描述

    如题,已知一个数列,你需要进行下面两种操作:

    1.将某区间每一个数加上$x$

    2.求出某区间每一个数的和

    输入格式

    第一行包含两个整数N、M,分别表示该数列数字的个数和操作的总个数。

    第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。

    接下来M行每行包含3或4个整数,表示一个操作,具体如下:

    操作1: 格式:1 $x$ $y$ $k$ 含义:将区间$[x,y]$内每个数加上$k$

    操作2: 格式:2 $x$ $y$ 含义:输出区间$[x,y]$内每个数的和

    输出格式

    输出包含若干行整数,即为所有操作2的结果。

     

    1.构建树
    更新$a_i$的值时,需要对所有包含$i$这个位置的结点的值重新计算。
    对于线段树中的每一个非叶子节点$[a,b]$,它的左儿子表示的区间为$[a,(a+b)/2]$,右儿子表示的区间为$[(a+b)/2+1,b]$。我们就可以得出节点i的权值=她的左儿子权值+她的右儿子权值。一颗二叉树,她的左儿子和右儿子编号分别是她*2和她*2+1
     1 inline void add(int x, int l, int r, ll y)
     2 { 
     3     c[x] += (r - l + 1) * y;
     4     lazy[x] += y; 
     5 } 
     6 inline void pushup(int x)
     7 { 
     8     c[x] = c[x*2] + c[x*2+1]; 
     9 }
    10 inline void pushdown(int x, int l, int r)
    11 { 
    12     add(x*2, l, (l+r)/2, lazy[x]);
    13     add(x*2+1, (l+r)/2 + 1, r, lazy[x]);
    14     lazy[x] = 0; 
    15 }
    16 inline void build(int x, int l, int r)
    17 {
    18     if(l == r) 
    19     return c[x] = a[l], void();
    20     build(x*2, l, (l+r)/2);
    21     build(x*2+1, (l+r)/2 + 1, r);
    22     pushup(x);
    23 }

    2.区间修改

    1、如果这个区间被完全包括在目标区间里面,直接修改整个区间

    2、如果这个区间的左儿子和目标区间有交集,那么修改左儿子

    3、如果这个区间的右儿子和目标区间有交集,那么修改右儿子

     1 inline void revise(int x, int l, int r, int l1, int r1, ll y){
     2     if(l1 <= l && r <= r1) 
     3         return add(x, l, r, y); 
     4     if(lazy[x]) 
     5         pushdown(x, l, r);
     6     if(l1 <= (l+r)/2) 
     7         revise(x*2, l, (l+r)/2, l1, r1, y);
     8     if(r1 > (l+r)/2)
     9         revise(x*2+1, (l+r)/2 + 1, r, l1, r1, y);
    10     pushup(x);
    11 }

    3.区间查询

    1、如果这个区间被完全包括在目标区间里面,直接返回这个区间的值

    2、如果这个区间的左儿子和目标区间有交集,那么搜索左儿子

    3、如果这个区间的右儿子和目标区间有交集,那么搜索右儿子

     1 inline ll query(int x, int l, int r, int l1, int r1){
     2     if(l1 <= l && r <= r1) 
     3         return c[x]; 
     4     if(lazy[x]) 
     5         pushdown(x, l, r);
     6     ll sum = 0;
     7     if(l1 <= (l+r)/2) 
     8         sum += query(x*2, l, (l+r)/2, l1, r1);
     9     if(r1 > (l+r)/2) 
    10         sum += query(x*2+1, (l+r)/2 + 1, r, l1, r1);
    11     return sum; 
    12 }

    这道题的完整代码:

     1 #include <algorithm>
     2 #include <cmath>
     3 #include <cstdio>
     4 #include <iostream>
     5 #include <cstring>
     6 #define ll long long
     7 using namespace std;
     8 const int N = 500000;
     9 int n, m, l, r, ans;
    10 ll k, a[N], c[N], lazy[N]; 
    11 inline void add(int x, int l, int r, ll y)
    12 { 
    13     c[x] += (r - l + 1) * y;
    14     lazy[x] += y; 
    15 } 
    16 inline void pushup(int x)
    17 { 
    18     c[x] = c[x*2] + c[x*2+1]; 
    19 }
    20 inline void pushdown(int x, int l, int r)
    21 { 
    22     add(x*2, l, (l+r)/2, lazy[x]);
    23     add(x*2+1, (l+r)/2 + 1, r, lazy[x]);
    24     lazy[x] = 0; 
    25 }
    26 inline void build(int x, int l, int r)
    27 {
    28     if(l == r) 
    29     return c[x] = a[l], void();
    30     build(x*2, l, (l+r)/2);
    31     build(x*2+1, (l+r)/2 + 1, r);
    32     pushup(x);
    33 }
    34 inline ll query(int x, int l, int r, int l1, int r1){
    35     if(l1 <= l && r <= r1) 
    36         return c[x]; 
    37     if(lazy[x]) 
    38         pushdown(x, l, r);
    39     ll sum = 0;
    40     if(l1 <= (l+r)/2) 
    41         sum += query(x*2, l, (l+r)/2, l1, r1);
    42     if(r1 > (l+r)/2) 
    43         sum += query(x*2+1, (l+r)/2 + 1, r, l1, r1);
    44     return sum; 
    45 }
    46 inline void revise(int x, int l, int r, int l1, int r1, ll y){
    47     if(l1 <= l && r <= r1) 
    48         return add(x, l, r, y); 
    49     if(lazy[x]) 
    50         pushdown(x, l, r);
    51     if(l1 <= (l+r)/2) 
    52         revise(x*2, l, (l+r)/2, l1, r1, y);
    53     if(r1 > (l+r)/2)
    54         revise(x*2+1, (l+r)/2 + 1, r, l1, r1, y);
    55     pushup(x);
    56 }
    57 
    58 int main(){
    59     scanf("%d %d", &n, &m); 
    60     for (int i = 1;i <= n;i++) 
    61         scanf("%lld", &a[i]);
    62     build(1, 1, n);
    63     for (int i = 1;i <= m;i++){
    64         scanf("%d %d %d", &ans, &l, &r);
    65         if(ans & 1) 
    66             scanf("%lld", &k), revise(1, 1, n, l, r, k);
    67         else
    68             printf("%lld
    ", query(1, 1, n, l, r));
    69     }
    70     return 0;
    71 }

    *接下来附上几个基本代码:

    一、区间最值

    1.单点替换:

     1 const int M=100001;
     2 LL a[M];
     3 LL MAX[M<<2];
     4 #define lson l,m,rt<<1
     5 #define rson m+1,r,rt<<1|1
     6 void update(int rt){
     7     MAX[rt]=max(MAX[rt<<1],MAX[rt<<1|1]);
     8 }
     9 void build(int l,int r,int rt){
    10     if (l==r) {
    11         MAX[rt]=a[l];
    12         return;
    13     }
    14     int m=(l+r)>>1;
    15     build(lson);
    16     build(rson);
    17     update(rt);
    18 }
    19 void modify(int l,int r,int rt,int p,int v){
    20     if (l==r) {
    21         MAX[rt]=v;
    22         return;
    23     }
    24     int m=(l+r)>>1;
    25     if (p<=m) modify(lson,p,v);
    26     else modify(rson,p,v);
    27     update(rt);
    28 }
    29 
    30 int query(int l,int r,int rt,int nowl,int nowr) {
    31     if (nowl<=l && r<=nowr) return MAX[rt];
    32     int ans=INT_MIN;
    33     int m=(l+r)>>1;
    34     if (nowl<=m) ans=max(ans,query(lson,nowl,nowr));
    35     if (m<nowr) ans=max(ans,query(rson,nowl,nowr));
    36     return ans;
    37 }

    二、区间和

    区间和是线段树可维护的最基本的信息,其他所有线段树可维护的序列信息,都是以区间和为模板建立的。

    1.单点增减、单点替换:

     1 #define lson l,m,rt<<1
     2 #define rson m+1,r,rt<<1|1
     3 void update(int rt){
     4     sum[rt]=sum[rt<<1]+sum[rt<<1|1];//sum[rt]表示rt节点所包含的区间信息,此处为区间和
     5 }
     6 void build(int l,int r,int rt){ //构造线段树
     7     if (l==r) {
     8         sum[rt]=a[l];
     9         return;
    10     }
    11     int m=(l+r)>>1;
    12     build(lson);
    13     build(rson);
    14     update(rt);
    15 }
    16 void modify(int l,int r,int rt,int p,LL v){ //将p位置修改为v
    17     if (l==r) {
    18         sum[rt]=v;//如果是将p位置的数+v,则此句应为sum[rt]+=v;
    19         return;
    20     }
    21     int m=(l+r)>>1;
    22     if (p<=m) modify(lson,p,v);
    23     else modify(rson,p,v);
    24     update(rt);
    25 }
    26 
    27 LL query(int l,int r,int rt,int nowl,int nowr) { //询问[nowl,nowr]的信息
    28     if (nowl<=l && r<=nowr) return sum[rt];
    29     LL ans=0;
    30     int m=(l+r)>>1;
    31     if (nowl<=m) ans+=query(lson,nowl,nowr);
    32     if (m<nowr) ans+=query(rson,nowl,nowr);
    33     return ans;
    34 }

    2.区间增减、区间替换:(测试:洛谷P3372)

     1 const int M=100001;
     2 LL a[M];
     3 LL sum[M<<2],lazy[M<<2];
     4 #define lson l,m,rt<<1
     5 #define rson m+1,r,rt<<1|1
     6 void update(int rt){
     7     sum[rt]=sum[rt<<1]+sum[rt<<1|1];
     8 }
     9 void build(int l,int r,int rt){
    10     lazy[rt]=0;//懒标记
    11     if (l==r) {
    12         sum[rt]=a[l];
    13         return;
    14     }
    15     int m=(l+r)>>1;
    16     build(lson);
    17     build(rson);
    18     update(rt);
    19 }
    20 void clean(int rt,int len){//懒标记下移
    21     if(lazy[rt]){
    22         lazy[rt<<1]+=lazy[rt];//若为区间替换则为“=”
    23         lazy[rt<<1|1]+=lazy[rt];//同上
    24         sum[rt<<1]+=lazy[rt]*(len-(len>>1));//同上
    25         sum[rt<<1|1]+=lazy[rt]*(len>>1);//同上
    26         lazy[rt]=0;
    27     }
    28 }
    29 void modify(int l,int r,int rt,int nowl,int nowr,LL v){
    30     int len=r-l+1;
    31     if (nowl<=l&&r<=nowr) {
    32         lazy[rt]+=v;//若为区间替换则为“=”
    33         sum[rt]+=v*len;//同上
    34         return;
    35     }
    36     clean(rt,len);//每次分治前需要下移懒标记,不分治就不下移
    37     int m=(l+r)>>1;
    38     if(nowl<=m)modify(lson,nowl,nowr,v);
    39     if(m<nowr)modify(rson,nowl,nowr,v);
    40     update(rt);
    41 }
    42 LL query(int l,int r,int rt,int nowl,int nowr) {
    43     if (nowl<=l && r<=nowr) return sum[rt];
    44     clean(rt,r-l+1);//同上
    45     LL ans=0;
    46     int m=(l+r)>>1;
    47     if (nowl<=m) ans+=query(lson,nowl,nowr);
    48     if (m<nowr) ans+=query(rson,nowl,nowr);
    49     return ans;
    50 }

    3.区间加减乘混合(测试:洛谷P3373)

     1 typedef long long LL;
     2 const int M=100001;
     3 LL a[M];
     4 LL sum[M<<2],add[M<<2],c[M<<2];
     5 LL p;//模数
     6 #define lson l,m,rt<<1
     7 #define rson m+1,r,rt<<1|1
     8 void update(int rt){
     9     sum[rt]=(sum[rt<<1]+sum[rt<<1|1])%p;
    10 }
    11 void build(int l,int r,int rt){
    12     add[rt]=0;c[rt]=1;//加法懒标记和乘法懒标记
    13     if (l==r) {
    14         sum[rt]=a[l];
    15         return;
    16     }
    17     int m=(l+r)>>1;
    18     build(lson);
    19     build(rson);
    20     update(rt);
    21 }
    22 void clean(int rt,int len){
    23     if(c[rt]!=1){//先下移乘法标记,再下移加法标记
    24         c[rt<<1]=c[rt<<1]*c[rt]%p;
    25         c[rt<<1|1]=c[rt<<1|1]*c[rt]%p;
    26         add[rt<<1]=add[rt<<1]*c[rt]%p;//加法标记也要乘上乘数
    27         add[rt<<1|1]=add[rt<<1|1]*c[rt]%p;
    28         sum[rt<<1]=sum[rt<<1]*c[rt]%p;
    29         sum[rt<<1|1]=sum[rt<<1|1]*c[rt]%p;
    30         c[rt]=1;
    31     }
    32     if(add[rt]){
    33         add[rt<<1]=(add[rt<<1]+add[rt])%p;
    34         add[rt<<1|1]=(add[rt<<1|1]+add[rt])%p;
    35         sum[rt<<1]+=add[rt]*(len-(len>>1));sum[rt<<1]%=p;
    36         sum[rt<<1|1]+=add[rt]*(len>>1);sum[rt<<1|1]%=p;
    37         add[rt]=0;
    38     }
    39 }
    40 void modify(int l,int r,int rt,int nowl,int nowr,LL v){//区间加法
    41     int len=r-l+1;
    42     if (nowl<=l&&r<=nowr) {
    43         add[rt]=(add[rt]+v)%p;
    44         sum[rt]=(sum[rt]+v*len%p)%p;
    45         return;
    46     }
    47     clean(rt,len);
    48     int m=(l+r)>>1;
    49     if(nowl<=m)modify(lson,nowl,nowr,v);
    50     if(m<nowr)modify(rson,nowl,nowr,v);
    51     update(rt);
    52 }
    53 void modify_(int l,int r,int rt,int nowl,int nowr,LL v){//区间乘法
    54     int len=r-l+1;
    55     if(nowl<=l&&r<=nowr){
    56         c[rt]=c[rt]*v%p;
    57         add[rt]=add[rt]*v%p;
    58         sum[rt]=sum[rt]*v%p;
    59         return;
    60     }
    61     clean(rt,len);
    62     int m=(l+r)>>1;
    63     if(nowl<=m)modify_(lson,nowl,nowr,v);
    64     if(m<nowr)modify_(rson,nowl,nowr,v);
    65     update(rt);
    66 }
    67 LL query(int l,int r,int rt,int nowl,int nowr) {
    68     if (nowl<=l && r<=nowr) return sum[rt];
    69     clean(rt,r-l+1);
    70     LL ans=0;
    71     int m=(l+r)>>1;
    72     if (nowl<=m) ans=(ans+query(lson,nowl,nowr))%p; 
    73     if (m<nowr) ans=(ans+query(rson,nowl,nowr))%p;
    74     return ans;
    75 }

     

  • 相关阅读:
    软件工程问题清单
    问题清单
    2020软件工程3作业
    2020软件工程作业02
    2020软件工程作业01
    2020软件工程个人作业06——软件工程实践总结作业
    2020软件工程作业03
    2020软件工程作业05
    软件工程作业 疑问总结
    2020软件工程作业04
  • 原文地址:https://www.cnblogs.com/very-beginning/p/12242568.html
Copyright © 2011-2022 走看看