[HAOI2015]树上操作(树链剖分)

题目链接:P3178 [HAOI2015]树上操作

树链剖分模板题。。。不会戳

省选也出模板题,唉。。。

对于操作一,直接线段树单点修改即可。

对于操作二,根据同一子树节点编号连续的性质,直接区间修改即可。

对于操作三,就是查询1->x的答案(太模板了。。。)

#include <iostream>
#include <cstdio>
typedef long long ll;
using namespace std;
const ll N = 100010;
struct seg_tree{
    ll val, tag, l, r;
}st[4 * N];
struct node{
    ll pre, to;
}edge[2 * N];
ll head[N], tot;
ll n, m;
ll w[N], sz[N], pos[N], len, bl[N], dep[N], fa[N], lst[N];
namespace chain{
    void dfs1(ll x, ll f) {
        sz[x] = 1;
        for (ll i = head[x]; i; i = edge[i].pre) {
            ll y = edge[i].to;
            if (y == f) continue;
            dep[y] = dep[x] + 1;
            fa[y] = x;
            dfs1(y, x);
            sz[x] += sz[y];
        }
    }
    void dfs2(ll x, ll chain) {
        ll k = 0;
        lst[x] = pos[x] = ++len;
        bl[x] = chain;
        for (ll i = head[x]; i; i = edge[i].pre) {
            ll y = edge[i].to;
            if (dep[y] < dep[x]) continue;
            if (sz[y] > sz[k]) {
                k = y;
            }
        }
        if (k) dfs2(k, chain);
        lst[x] = max(lst[x], lst[k]);
        for (ll i = head[x]; i; i = edge[i].pre) {
            ll y = edge[i].to;
            if (dep[y] < dep[x] || k == y) continue;
            dfs2(y, y);
            lst[x] = max(lst[x], lst[y]);
        }
    }
}using namespace chain;
namespace segment_tree{
    void build(ll x, ll l, ll r) {
        st[x].l = l, st[x].r = r;
        if (l == r) return;
        ll mid = (l + r) >> 1;
        build(x << 1, l, mid);
        build(x << 1 | 1, mid + 1, r);
    }
    void update(ll x) {
        if (st[x].tag) {
            st[x << 1].tag += st[x].tag;
            st[x << 1 | 1].tag += st[x].tag;
            st[x << 1].val += (st[x << 1].r - st[x << 1].l + 1) * st[x].tag;
            st[x << 1 | 1].val += (st[x << 1 | 1].r - st[x << 1 | 1].l + 1) * st[x].tag;
            st[x].tag = 0;
        }
    }
    //单点修改 
    void change1(ll x, ll p, ll v) {
        ll l = st[x].l, r = st[x].r;
        if (l == r) {
            st[x].val += v;
            return;
        }
        ll mid = (l + r) >> 1;
        update(x);
        if (p <= mid) change1(x << 1, p, v);
        else change1(x << 1 | 1, p, v);
        st[x].val = st[x << 1].val + st[x << 1 | 1].val;
    }
    //区间修改 
    void change2(ll x, ll L, ll R, ll v) {
        ll l = st[x].l, r = st[x].r;
        if (r < L || l > R) return;
        if (L <= l && r <= R) {
            st[x].tag += v;
            st[x].val += (r - l + 1) * v;
            return;
        }
        update(x);
        change2(x << 1, L, R, v);
        change2(x << 1 | 1, L, R, v);
        st[x].val = st[x << 1].val + st[x << 1 | 1].val;
    }
    //区间求和 
    ll ask(ll x, ll L, ll R) {
        ll l = st[x].l, r = st[x].r;
        if (r < L || l > R) return 0;
        if (L <= l && r <= R) {
            return st[x].val;
        }
        update(x);
        return ask(x << 1, L, R) + ask(x << 1 | 1, L, R);
    }
    ll Qsum(ll x) {
        ll ret = 0;
        while (bl[x] != bl[1]) {
            ret += ask(1, pos[bl[x]], pos[x]);
            x = fa[bl[x]];
        }
        ret += ask(1, pos[bl[x]], pos[x]);
        return ret;
    }
}using namespace segment_tree;
void add(ll u, ll v) {
    edge[++tot] = node{head[u], v};
    head[u] = tot;
}
int main() {
    cin >> n >> m;
    for (ll i = 1; i <= n; i++) cin >> w[i];
    for (ll i = 1, a, b; i < n; i++) {
        cin >> a >> b;
        add(a, b);
        add(b, a);
    }
    dfs1(1, 0);
    dfs2(1, 1);
    build(1, 1, len);
    for (ll i = 1; i <= n; i++) {
        change1(1, pos[i], w[i]);
    }
    while (m--) {
        ll opt, x, a;
        cin >> opt >> x;
        if (opt == 1) {
            cin >> a;
            change1(1, pos[x], a);
        } else if (opt == 2) {
            cin >> a;
            change2(1, pos[x], lst[x], a);
        } else {
            cout << Qsum(x) << "\n";
        }
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/zcr-blog/p/12620252.html