线段树入门之单点更新

Posted codedecision

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树入门之单点更新相关的知识,希望对你有一定的参考价值。

  • 作者:zifeiy
  • 标签:线段树

单点更新 :最最基础的线段树,只更新叶子节点,然后把信息用 push_up(int rt) 这个函数更新上来

HDU1166 敌兵布阵

#include <bits/stdc++.h>
using namespace std;
#define lson l, m, rt<<1
#define rson m+1, r, rt<<1|1
const int maxn = 50050;
int sum[maxn<<2];
inline void push_up(int rt) { sum[rt] = sum[rt<<1] + sum[rt<<1|1]; }
void build(int l, int r, int rt) {
    if (l == r) { cin >> sum[rt]; return; }
    int m = (l + r) >> 1;
    build(lson); build(rson); push_up(rt);
}
void update(int p, int add, int l, int r, int rt) {
    if (l == r) { sum[rt] += add; return; }
    int m = (l + r) >> 1;
    (p <= m) ? update(p, add, lson) : update(p, add, rson);
    push_up(rt);
}
int query(int L, int R, int l, int r, int rt) {
    if (L <= l && r <= R) return sum[rt];
    int m = (l + r) >> 1, res = 0;
    if (L <= m) res += query(L, R, lson);
    if (R > m) res += query(L, R, rson);
    return res;
}
int T, n, a, b; char op[10];
int main() {
    scanf("%d", &T);
    for (int cas = 1; cas <= T; cas ++) {
        printf("Case %d:
", cas);
        scanf("%d", &n);
        build(1, n, 1);
        while (scanf("%s", op) && op[0] != 'E') {
            scanf("%d%d", &a, &b);
            if (op[0] == 'A') update(a, b, 1, n, 1);
            else if (op[0] == 'S') update(a, -b, 1, n, 1);
            else printf("%d
", query(a, b, 1, n, 1));
        }
    }
    return 0;
}

HDU1754 I Hate It

#include <bits/stdc++.h>
using namespace std;
#define lson l, m, rt<<1
#define rson m+1, r, rt<<1|1
const int maxn = 200020;
int maxv[maxn<<2];
inline void push_up(int rt) { maxv[rt] = max(maxv[rt<<1], maxv[rt<<1|1]); }
void build(int l, int r, int rt) {
    if (l == r) { scanf("%d", &maxv[rt]); return; }
    int m = (l + r) >> 1;
    build(lson); build(rson); push_up(rt);
}
void update(int p, int val, int l, int r, int rt) {
    if (l == r) { maxv[rt] = val; return; }
    int m = (l + r) >> 1;
    (p <= m) ? update(p, val, lson) : update(p, val, rson);
    push_up(rt);
}
int query(int L, int R, int l, int r, int rt) {
    if (L <= l && r <= R) return maxv[rt];
    int m = (l + r) >> 1, res = 0;
    if (L <= m) res = max(res, query(L, R, lson));
    if (R > m) res = max(res, query(L, R, rson));
    return res;
}
int n, m, a, b; char op[10];
int main() {
    while (~scanf("%d%d", &n, &m)) {
        build(1, n, 1);
        while (m --) {
            scanf("%s%d%d", op, &a, &b);
            if (op[0] == 'U') update(a, b, 1, n, 1);
            else printf("%d
", query(a, b, 1, n, 1));
        }
    }
    return 0;
}

HDU1394 Minimum Inversion Number

  • 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1394
  • 题目大意:给你一个长度为n的数组,你可以选择任意次数将数组的最后一个元素放到数组的最前面的那个位置(每次操作称为一次Inversion),求所有Inversion操作中数组的最小逆序对。
  • 思路:用 (O(n imes log n)) 复杂度求出最初逆序对,就可以用 (O(1)) 的复杂度分别递推求出其他解。
  • 线段树功能:
    • update:单点更新;
    • query:区间求和
#include <bits/stdc++.h>
using namespace std;
#define lson l, m, rt<<1
#define rson m+1, r, rt<<1|1
const int maxn = 5050;
int sum[maxn<<2];
inline void push_up(int rt) { sum[rt] = sum[rt<<1] + sum[rt<<1|1]; }
void build(int l, int r, int rt) {
    if (l == r) { sum[rt] = 0; return; }
    int m = (l + r) >> 1;
    build(lson); build(rson); push_up(rt);
}
void add(int p, int l, int r, int rt) {
    if (l == r) { sum[rt] ++; return; }
    int m = (l + r) >> 1;
    (p <= m) ? add(p, lson) : add(p, rson);
    push_up(rt);
}
int query(int L, int R, int l, int r, int rt) {
    if (L <= l && r <= R) return sum[rt];
    int m = (l + r) >> 1, res = 0;
    if (L <= m) res += query(L, R, lson);
    if (R > m) res += query(L, R, rson);
    return res;
}
int n, a[maxn], p[maxn], res, tmp;
int main() {
    while (~scanf("%d", &n)) {
        for (int i = 0; i < n; i ++) {
            cin >> a[i];
            p[ a[i] ] = i;
        }
        build(0, n-1, 1);
        res = 0;
        for (int i = 0; i < n; i ++) {
            res += query(p[i], n-1, 0, n-1, 1);
            add(p[i], 0, n-1, 1);
        }
        tmp = res;
        for (int i = 0; i < n-1; i ++) {
            tmp += (n - 1 - a[i]) - a[i];
            res = min(res, tmp);
        }
        printf("%d
", res);
    }
    return 0;
}

HDU2795 Billboard

  • 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2795
  • 题意: (h imes w) 的黑板,要贴一些一些 (1 imes L) 的海报,海报要尽可能往上放,同一高度要尽可能往左放,求每张海报放的位置。
  • 思路:每次找到最左边满足大于等于L的位置,然后减去L
  • 线段树功能:
    • query:区间求最大值的位置(直接把 update 的操作写在 query 里面了)
#include <bits/stdc++.h>
using namespace std;
#define lson l, m, rt<<1
#define rson m+1, r, rt<<1|1
const int maxn = 200020;
int n, h, w, maxv[maxn<<2];
inline void push_up(int rt) { maxv[rt] = max(maxv[rt<<1], maxv[rt<<1|1]); }
void build(int l, int r, int rt) {
    maxv[rt] = w;
    if (l == r) return;
    int m = (l + r) >> 1;
    build(lson); build(rson);
}
int query(int x, int l, int r, int rt) {
    if (l == r) { maxv[rt] -= x; return l; }
    int m = (l + r) >> 1;
    int res = (maxv[rt<<1] >= x) ? query(x, lson) : query(x, rson);
    push_up(rt);
    return res;
}
int main() {
    while (~scanf("%d%d%d", &h, &w, &n)) {
        if (h > n) h = n;
        build(1, h, 1);
        while (n --) {
            int x;
            scanf("%d", &x);
            if (maxv[1] < x) puts("-1");
            else printf("%d
", query(x, 1, h, 1));
        }
    }
    return 0;
}

练习

以上是关于线段树入门之单点更新的主要内容,如果未能解决你的问题,请参考以下文章

ACM入门之线段树习题

线段树基础

HDU 1166 敌兵布阵 线段树单点更新求和

HDU 1754 I Hate It(线段树之单点更新,区间最值)

HDU 1754 I Hate It 线段树单点更新求最大值

HDU 1754 I Hate It(线段树之单点更新 区间最值查询)