线段树入门之单点更新

  • 作者: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:\n", 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\n", 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\n", 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 \times \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\n", res);
    }
    return 0;
}

HDU2795 Billboard

  • 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2795
  • 题意: \(h \times w\) 的黑板,要贴一些一些 \(1 \times 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\n", query(x, 1, h, 1));
        }
    }
    return 0;
}

练习

posted @ 2019-10-15 12:25  codedecision  阅读(268)  评论(0编辑  收藏  举报