洛谷P3178 [HAOI2015]树上操作 题解 树链剖分+线段树

题目链接:https://www.luogu.org/problem/P3178

这道题目是一道树链剖分的模板题。
但是在解决这道问题的同事刷新了我的两个认识:

第一个认识是:树链剖分不光可以处理链,还可以处理 子树 ,因为:
节点 u 的子树中所有的点的编号都覆盖在 seg[u]seg[u]+size[u]-1 这个区间内!

第二个认识是:线段树延迟操作的延迟标记不是标记自己,也就是说:
lazy[rt] 并不是标记本身的延迟值,而是说 rt 本身有多少个延迟值没有传递给 rt<<1rt<<1|1 的。

然后这道题目就是一道裸树链剖分题,运用到了子树更新和延迟操作。
实现代码如下:

#include 
using namespace std;
#define INF (1<<29)
const int maxn = 100010;
int fa[maxn],
    dep[maxn],
    size[maxn],
    son[maxn],
    top[maxn],
    seg[maxn], seg_cnt,
    rev[maxn],
    n, w[maxn];
long long sumv[maxn<<2], lazy[maxn<<2];
vector g[maxn];
void dfs1(int u, int p) {
    size[u] = 1;
    for (vector::iterator it = g[u].begin(); it != g[u].end(); it ++) {
        int v = (*it);
        if (v == p) continue;
        fa[v] = u;
        dep[v] = dep[u] + 1;
        dfs1(v, u);
        size[u] += size[v];
        if (size[v] >size[son[u]]) son[u] = v;
    }
}
void dfs2(int u, int tp) {
    seg[u] = ++seg_cnt;
    rev[seg_cnt] = u;
    top[u] = tp;
    if (son[u]) dfs2(son[u], tp);
    for (vector::iterator it = g[u].begin(); it != g[u].end(); it ++) {
        int v = (*it);
        if (v == fa[u] || v == son[u]) continue;
        dfs2(v, v);
    }
}
#define lson l, mid, rt<<1
#define rson mid+1, r, rt<<1|1
void push_down(int rt, int len) {
    if (lazy[rt]) {
        int l_len=len-len/2, r_len = len/2;
        lazy[rt<<1] += lazy[rt];
        lazy[rt<<1|1] += lazy[rt];
        sumv[rt<<1] += lazy[rt] * l_len;
        sumv[rt<<1|1] += lazy[rt] * r_len;
        lazy[rt] = 0;
    }
}
void push_up(int rt) {
    sumv[rt] = sumv[rt<<1] + sumv[rt<<1|1];
}
void build(int l, int r, int rt) {
    int mid = (l + r) / 2;
    if (l == r) {
        sumv[rt] = w[rev[l]];
        return;
    }
    build(lson); build(rson);
    push_up(rt);
}
void update(int L, int R, long long v, int l, int r, int rt) {
    if (L <= l && r <= R) {
        sumv[rt] += (r-l+1) * v;
        lazy[rt] += v;
        return;
    }
    push_down(rt, r-l+1);
    int mid = (l + r) / 2;
    if (L <= mid) update(L, R, v, lson);
    if (R > mid) update(L, R, v, rson);
    push_up(rt);
}
long long query_sum(int L, int R, int l, int r, int rt) {
    if (L <= l && r <= R) return sumv[rt];
    push_down(rt, r-l+1);
    int mid = (l + r) / 2;
    long long tmp = 0;
    if (L <= mid) tmp += query_sum(L, R, lson);
    if (R > mid) tmp += query_sum(L, R, rson);
    return tmp;
}
long long ask_sum(int u, int v) {
    long long res = 0;
    while (top[u] != top[v]) {
        if (dep[top[u]] < dep[top[v]]) swap(u, v);
        res += query_sum(seg[top[u]], seg[u], 1, n, 1);
        u = fa[top[u]];
    }
    if (dep[u] < dep[v]) swap(u, v);
    res += query_sum(seg[v], seg[u], 1, n, 1);
    return res;
}
int m, op, x, a;
string s;
int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i ++) cin >> w[i];
    for (int i = 1; i < n; i ++) {
        int u, v;
        cin >> u >> v;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    dep[1] = fa[1] = 1;
    dfs1(1, -1);
    dfs2(1, 1);
    build(1, n, 1);
    while (m --) {
        cin >> op;
        if (op == 1) {
            cin >> x >> a;
            update(seg[x], seg[x], a, 1, n, 1);
        }
        else if (op == 2) {
            cin >> x >> a;
            update(seg[x], seg[x]+size[x]-1, a, 1, n, 1);
        }
        else {
            cin >> x;
            cout << ask_sum(1, x) << endl;
        }
    }
    return 0;
}

作者:zifeiy

你可能感兴趣的:(洛谷P3178 [HAOI2015]树上操作 题解 树链剖分+线段树)