线段树 (模板)

功能

\(log_2n\)时间复杂度对区间进行加减乘求和等等操作的数据结构。

实现

主要参考的是oi_wiki教程。
记录一下板子(即HDU1166 - 敌兵布阵的代码)

注意事项

  1. 开4倍空间,再大点更好。
  2. 叶结点不要pushdown,否则可能会越界(因为只开了4倍空间)
//L,R均代表待处理的区间。
//lef,rig均代表范围。
//单点更新(覆盖),区间求和
#include <cstdio>
#include <iostream>
#include <cstring>

using namespace std;

typedef long long ll;

const ll maxn = 50001;

ll sum[maxn << 2];
ll lazy[maxn << 2];

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

void pushdown(int rt, int len) {
    if(lazy[rt]) {
        lazy[rt << 1] += lazy[rt];
        lazy[rt << 1 | 1] += lazy[rt];
        sum[rt << 1] += lazy[rt << 1] * (len - (len >> 1));
        sum[rt << 1 | 1] += lazy[rt << 1 | 1] * (len >> 1);
        lazy[rt] = 0;
    }
}

void update(int L, int R, int lef, int rig, int rt, ll val) {
    if(lef >= L && rig <= R) {
        sum[rt] += val * (rig - lef + 1);
        lazy[rt] += val;
        return ;
    }
    pushdown(rt, rig - lef + 1);
    int mid = (lef + rig) / 2;
    if(L <= mid) update(L, R, lef, mid, rt << 1, val);
    if(R > mid) update(L, R, mid + 1, rig, rt << 1 | 1, val);
    pushup(rt);
}

void build(int lef, int rig, int rt) {
    if(lef > rig) return ;
    lazy[rt] = 0;
    if(lef == rig) {
        ll tmp;
        scanf("%lld", &tmp);
        sum[rt] = tmp;
        return ;
    }
    int mid = (lef + rig) / 2;
    build(lef, mid, rt << 1);
    build(mid + 1, rig, rt << 1 | 1);
    pushup(rt);
}

ll query(int L, int R, int lef, int rig, int rt) {
    if(lef >= L && rig <= R) return sum[rt];
    pushdown(rt, rig - lef + 1);
    int mid = (lef + rig) / 2;
    ll res = 0;
    if(L <= mid) res += query(L, R, lef, mid, rt << 1);
    if(R > mid) res += query(L, R, mid + 1, rig, rt << 1 | 1);
    return res;
}


int main() {
    int T = 0;
    char q[10];
    scanf("%d", &T);
    for(int cases = 1; cases <= T; cases++) {
        printf("Case %d:\n", cases);
        int n;
        scanf("%d", &n);
        build(1, n, 1);
        while(1) {
            scanf("%s", q);
            if(!strcmp(q, "Add")) {
                int pos;
                ll val;
                scanf("%d%lld", &pos, &val);
                update(pos, pos, 1, n, 1, val);
            }
            else if(!strcmp(q, "Sub")) {
                int pos;
                ll val;
                scanf("%d%lld", &pos, &val);
                update(pos, pos, 1, n, 1, -val);
            }
            else if(!strcmp(q, "Query")) {
                int L, R;
                scanf("%d%d", &L, &R);
                printf("%lld\n", query(L, R, 1, n, 1));
            }
            else if(!strcmp(q, "End")) break;
        }
    }
}

其它

对于大范围的区间操作,可以用离散化的技巧。但是这主要处理以后线段树就不支持在线操作了。
似乎有一种动态开点的方法可以解决这问题,有时间补上。

posted @ 2020-04-14 15:18  limil  阅读(100)  评论(0编辑  收藏  举报