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 }

     

  • 相关阅读:
    NYOJ 10 skiing DFS+DP
    51nod 1270 数组的最大代价
    HDU 4635 Strongly connected
    HDU 4612 Warm up
    POJ 3177 Redundant Paths
    HDU 1629 迷宫城堡
    uva 796
    uva 315
    POJ 3180 The Cow Prom
    POJ 1236 Network of Schools
  • 原文地址:https://www.cnblogs.com/very-beginning/p/12242568.html
Copyright © 2011-2022 走看看