线段树+lazy标记

  • 线段树基于分治思想的二叉树,用于区间信息统计
    特点:
  1. 每个节点代表区间
  2. 唯一根节点,也就是全部区间
  3. 叶节点是长度为1的子区间,也就是所代表数组上的一个点
  • 存线段树的结构体数组长度一般是 线段树所代表数组长度的4倍.
    1
    2
    3
    4
    5
    const int maxn = 1e3;
    struct segmentTree {
    int l,r;
    int dat;
    } e[4 * maxn];
  • 线段树3个操作
  1. 建树
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    void build(int p,int l,int r) {
    e[p].l = l; e[p].r = r;
    if (l == r) {
    e[p].dat = a[l]; // 存原数组的值
    return ;
    }
    int mid = (l+r)/2;
    build(p*2,l,mid); //递归建左子树
    build(p*2+1,mid+1,r); //右子树
    e[p].dat = max(e[p*2].dat,e[p*2+1].dat); //从下到上(回溯)更新区间节点的最大值
    }
  2. 修改某节点值
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    void change(int p,int index,int val) {
    if (e[p].l == e[p].r) {
    e[p].dat = val;
    return ;
    }
    int mid = (e[p].l + e[p].r)/2;
    if (index <= mid) change(p*2,index,val);
    else change(p*2+1,index,val);
    e[p].dat = max(e[p*2].dat,e[p*2+1].dat);
    }
  3. 询问区间最值
1
2
3
4
5
6
7
8
int ask(int p,int l,int r) {
if (l<=e[p].l && e[p].r <= r) return e[p].dat;
int mid = (e[p].l + e[p].r)/2;
int val = -(1<<30);
if (l <= mid) val = max(val,ask(p*2,l,r));
if (r > mid) val = max(val,ask(p*2+1,l,r));
return val;
}

线段树例题

HDU 1166

  • 此线段树存的是区间和,不是区间最值
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    #include <iostream>
    #include <string.h>
    using namespace std;
    const int maxn = 2e5+5;

    int a[maxn];
    int n;

    struct segmentTree {
    int l,r;
    int dat;
    } e[4 * maxn];

    void build(int p,int l,int r) {
    e[p].l = l; e[p].r = r;
    if (l == r) {
    e[p].dat = a[l]; // 存原数组的值
    return ;
    }
    int mid = (l+r)/2;
    build(p*2 , l , mid); //递归建左子树
    build(p*2+1,mid+1,r); //右子树
    e[p].dat = e[p*2].dat + e[p*2+1].dat; //从下到上(回溯)更新区间节点的最大值
    }
    void change(int p,int index,int val) {
    if (e[p].l == e[p].r) {
    e[p].dat += val;
    return ;
    }
    int mid = (e[p].l + e[p].r)/2;
    if (index <= mid) change(p*2,index,val);
    else change(p*2+1,index,val);
    e[p].dat = e[p*2].dat + e[p*2+1].dat;
    }

    int ask(int p,int l,int r) {
    if (l <= e[p].l && e[p].r <= r) return e[p].dat;
    int mid = (e[p].l + e[p].r)/2;
    int val = 0;
    if (l <= mid) val += ask(p*2 ,l,r); // l,r 代表询问的区间
    if (r > mid) val += ask(p*2+1,l,r);
    return val;
    }

    int main() {
    // freopen("a.txt","r",stdin);
    int cnt=0;
    int times; scanf("%d",&times);
    while (times--) {
    scanf("%d",&n);
    memset(e,0,sizeof e);
    memset(a,0,sizeof a);
    for (int i=1; i<=n; i++) scanf("%d",&a[i]);
    build(1,1,n);
    char ch[10];
    printf("Case %d:\n",++cnt);
    while (scanf("%s",ch) && ch[0]!='E') {
    int l,r; scanf("%d%d",&l,&r);
    if (ch[0] == 'Q')
    printf("%d\n",ask(1,l,r));
    else if (ch[0] == 'A')
    change(1,l,r);
    else if (ch[0] == 'S')
    change(1,l,-r);
    }
    }
    return 0;
    }

  • 2019/12/15 17:18:15
  • 上面线段树写的这么挫,难怪线段树像白学了一样.一直手写不出来,这次重新写一下
  • 区间修改+区间查询:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    #include <bits/stdc++.h>
    using namespace std;
    using ll = long long;
    const int maxn = 4e5+10;
    int a[maxn/4],left_[maxn],right_[maxn];
    ll add[maxn],sum[maxn];

    void pushup(int p) { sum[p] = sum[p<<1] + sum[p<<1|1]; }

    void pushdown(int p) {
    if (add[p]) {
    sum[p<<1] += add[p]*(right_[p<<1] - left_[p<<1] + 1);
    sum[p<<1|1] += add[p]*(right_[p<<1|1] - left_[p<<1|1] + 1);
    add[p<<1] += add[p];
    add[p<<1|1] += add[p];
    add[p] = 0;
    }
    }

    void build(int p,int l,int r) {
    left_[p]=l,right_[p]=r;
    if (l == r) {
    sum[p] = a[l];
    return ;
    }
    int mid = (l + r) >> 1;
    build(p<<1,l,mid);
    build(p<<1|1,mid+1,r);
    pushup(p);
    }

    void change(int p,int l,int r,int val) {
    if (l<=left_[p] && right_[p]<=r) {
    sum[p] += (ll)val * (right_[p] - left_[p] + 1);
    add[p] += val;
    return ;
    }
    // 要向下继续找,则下传标记
    pushdown(p);
    int mid = (left_[p] + right_[p]) >> 1;
    if (l <= mid)
    change(p<<1,l,r,val);
    if (mid < r)
    change(p<<1|1,l,r,val);
    pushup(p);
    }

    ll query(int p,int l,int r) {
    if (l<=left_[p] && right_[p]<=r)
    return sum[p];
    pushdown(p);
    ll ans = 0;
    int mid = (left_[p] + right_[p]) >> 1;
    if (l <= mid)
    ans += query(p<<1,l,r);
    if (mid < r)
    ans += query(p<<1|1,l,r);
    return ans;
    }

    int n,m;

    int main() {
    [](){
    ::ios::sync_with_stdio(false);
    ::cin.tie(0);
    ::cout.tie(0);
    }();
    cin >> n >> m;
    for (int i=1; i<=n; ++i)
    cin >> a[i];
    build(1,1,n);
    while(m--) {
    int index,x,y,k;
    cin >> index;
    if (index == 1) {
    cin >> x >> y >> k;
    change(1,x,y,k);
    } else {
    cin >> x >> y;
    cout << query(1,x,y) << "\n";
    }
    }
    return 0;
    }


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!