洛谷 P3178 [HAOI2015] 树上操作-提高+/省选-

发布于:2025-09-07 ⋅ 阅读:(13) ⋅ 点赞:(0)

P3178 [HAOI2015] 树上操作

题目描述

有一棵点数为 N N N 的树,以点 1 1 1 为根,且树有点权。然后有 M M M 个操作,分为三种:

  • 操作 1 1 1:把某个节点 x x x 的点权增加 a a a
  • 操作 2 2 2:把某个节点 x x x 为根的子树中所有点的点权都增加 a a a
  • 操作 3 3 3:询问某个节点 x x x 到根的路径中所有点的点权和。

输入格式

第一行包含两个整数 N , M N,M N,M。表示点数和操作数。
接下来一行 N N N 个整数,表示树中节点的初始权值。
接下来 N − 1 N-1 N1 行每行两个正整数 f r o m , t o \mathit{from},\mathit{to} from,to, 表示该树中存在一条边 ( f r o m , t o ) (\mathit{from},\mathit{to}) (from,to)
再接下来 M M M 行,每行分别表示一次操作。其中第一个数表示该操作的种类,之后接这个操作的参数。

输出格式

对于每个询问操作,输出该询问的答案。答案之间用换行隔开。

输入输出样例 #1

输入 #1

5 5
1 2 3 4 5
1 2
1 4
2 3
2 5
3 3
1 2 1
3 5
2 1 2
3 3

输出 #1

6
9
13

说明/提示

对于 100 % 100\% 100% 的数据, 1 ≤ N , M ≤ 10 5 1\le N,M\le10^5 1N,M105,且所有输入数据的绝对值都不会超过 10 6 10^6 106

solution

代码

#include <iostream>
#include "bit"
#include "vector"
#include "unordered_set"
#include "set"
#include "queue"
#include "algorithm"
#include "bitset"
#include "cstring"

using namespace std;

/*
 * P3178 [HAOI2015] 树上操作
 * 题目大意:
 * 有一棵点数为 N 的树,以点 1 为根,且树有点权。然后有 M 个操作,分为三种,1≤N,M≤10^5:
 * 操作 1:把某个节点 x 的点权增加 a。
 * 操作 2:把某个节点 x 为根的子树中所有点的点权都增加 a。
 * 操作 3:询问某个节点 x 到根的路径中所有点的点权和。
 *
 * 思路:
 * 1 通过树链剖分将操作中树的节点的 dfn 映射成连续的若干段
 * 2 通过线段树处理区间修改和查询。
 */

typedef pair<int, int> pii;
typedef long long ll;

const int N = 1e5 + 5;

int n, m, val[N], d[N], dfn[N], top[N], id[N], t, fa[N], siz[N], son[N];
vector<int> e[N];

void dfs(int u, int p) {
    fa[u] = p;
    d[u] = d[p] + 1;
    siz[u] = 1;
    int Max = -1;
    for (int v: e[u]) {
        if (v == p) continue;
        dfs(v, u);
        siz[u] += siz[v];
        if (siz[v] > Max) Max = siz[v], son[u] = v;
    }
}

void dfs2(int u, int tp) {
    dfn[u] = ++t;
    id[t] = u;
    top[u] = tp;
    if (!son[u]) return;
    dfs2(son[u], tp);

    for (int v: e[u]) {
        if (v == fa[u] || v == son[u]) continue;
        dfs2(v, v);
    }
}

ll tag[N << 2], sum[N << 2];

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

void push_down(int rt, int l, int r) {
    int mid = l + r >> 1;
    sum[rt << 1] += tag[rt] * (mid + 1 - l);
    sum[rt << 1 | 1] += tag[rt] * (r - mid);
    tag[rt << 1] += tag[rt];
    tag[rt << 1 | 1] += tag[rt];
    tag[rt] = 0;
}

void build(int l, int r, int rt) {
    if (l == r) {
        sum[rt] = val[id[l]];
        return;
    }

    int mid = l + r >> 1;
    build(l, mid, rt << 1);
    build(mid + 1, r, rt << 1 | 1);
    push_up(rt);
}

void update(int L, int R, int l, int r, int rt, int v) {
    if (L <= l && r <= R) {
        sum[rt] += 1ll * (r - l + 1) * v;
        tag[rt] += v;
        return;
    }

    push_down(rt, l, r);
    int mid = l + r >> 1;
    if (L <= mid) update(L, R, l, mid, rt << 1, v);
    if (R > mid) update(L, R, mid + 1, r, rt << 1 | 1, v);
    push_up(rt);
}

ll search(int L, int R, int l, int r, int rt) {
    if (L <= l && r <= R) return sum[rt];
    push_down(rt, l, r);
    int mid = l + r >> 1;
    ll s = 0;
    if (L <= mid) s += search(L, R, l, mid, rt << 1);
    if (R > mid) s += search(L, R, mid + 1, r, rt << 1 | 1);
    return s;
}

int main() {
    cin >> n >> m;

    for (int i = 1; i <= n; i++) cin >> val[i];
    for (int i = 1, x, y; i < n; i++) cin >> x >> y, e[y].push_back(x), e[x].push_back(y);

    dfs(1, 0);
    dfs2(1, 1);

    build(1, n, 1);
    for (int i = 1, op, x, a; i <= m; i++) {
        cin >> op >> x;
        if (op == 1) {
            cin >> a;
            update(dfn[x], dfn[x], 1, n, 1, a);
        } else if (op == 2) {
            cin >> a;
            update(dfn[x], dfn[x] + siz[x] - 1, 1, n, 1, a);
        } else {
            ll s = 0;
            while (x) {
                s += search(dfn[top[x]], dfn[x], 1, n, 1);
                x = fa[top[x]];
            }
            cout << s << endl;
        }
    }
    return 0;
}

结果

在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到