【模板】P2590 [ZJOI2008]树的统计 · 树链剖分+线段树

题解

注意负数!!


在这里插入图片描述


#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int N = 2e5 + 10;
int n, m, k;
namespace chain {//树链剖分板子
    int a[N];//实际点权

    struct egde {
        int to, next;
    } e[N];
    int head[N], tot;

    void add(int u, int v) {//一次建双边
        e[++tot] = {v, head[u]};
        head[u] = tot;
        e[++tot] = {u, head[v]};
        head[v] = tot;
    }

    int dep[N];//深度
    int f[N];//父节点
    int son[N];//重儿子
    int sz[N];//子树大小

    void dfs1(int u, int fa) {
        f[u] = fa;
        dep[u] = dep[fa] + 1;
        sz[u] = 1;
        for (int i = head[u]; i; i = e[i].next) {
            int v = e[i].to;
            if (v != fa) {
                dfs1(v, u);
                sz[u] += sz[v];
                if (sz[v] > sz[son[u]])
                    son[u] = v;
            }
        }
    }

    int dfn = 0, id[N], top[N];
    int w[N];//树链剖分后的点权
    void dfs2(int u, int tp) {
        id[u] = ++dfn;
        top[u] = tp;
        w[dfn] = a[u];
        if (son[u])dfs2(son[u], tp);
        for (int i = head[u]; i; i = e[i].next) {
            int v = e[i].to;
            if (v != f[u] && v != son[u]) {
                dfs2(v, v);
            }
        }
    }
}
using namespace chain;


namespace Segment_Tree {//线段树板子
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1

    int Max[N * 4];//区间最大值
    int sum[N * 4];//区间和

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

    void build(int l, int r, int rt) {
        if (l == r) {
            sum[rt] = Max[rt] = w[l];//可能需要修改
            return;
        }
        int mid = l + r >> 1;
        build(lson);
        build(rson);
        pushup(rt);
    }

    void update(int pos, int t, int l, int r, int rt) {
        if (l == r) {
            sum[rt] = Max[rt] = t;
            return;
        }
        int mid = l + r >> 1;
        if (pos <= mid)update(pos, t, lson);
        else update(pos, t, rson);
        pushup(rt);
    }

    int query_sum(int L, int R, int l, int r, int rt) {
        if (L <= l && r <= R) {
            return sum[rt];
        }
        int mid = l + r >> 1;
        int res = 0;
        if (L <= mid)res += query_sum(L, R, lson);
        if (R > mid)res += query_sum(L, R, rson);
        return res;
    }

    int query_max(int L, int R, int l, int r, int rt) {
        if (L <= l && r <= R) {
            return Max[rt];
        }
        int mid = l + r >> 1;
        int res = -INF;//注意负数!!
        if (L <= mid)res = max(res, query_max(L, R, lson));
        if (R > mid)res = max(res, query_max(L, R, rson));
        return res;
    }
}
using namespace Segment_Tree;

int qsum(int u, int v) {
    int res = 0;
    while (top[u] != top[v]) {
        if (dep[top[u]] < dep[top[v]])swap(u, v);
        res += query_sum(id[top[u]], id[u], 1, n, 1);
        u = f[top[u]];
    }
    if (dep[u] > dep[v])swap(u, v);
    res += query_sum(id[u], id[v], 1, n, 1);
    return res;
}

int qmax(int u, int v) {
    int res = -INF;
    while (top[u] != top[v]) {
        if (dep[top[u]] < dep[top[v]])swap(u, v);
        res = max(res, query_max(id[top[u]], id[u], 1, n, 1));
        u = f[top[u]];
    }
    if (dep[u] > dep[v])swap(u, v);
    res = max(res, query_max(id[u], id[v], 1, n, 1));
    return res;
}

string op;

int main() {
    ios::sync_with_stdio(0);
    cin >> n;
    for (int i = 1, u, v; i < n; ++i) {
        cin >> u >> v;
        add(u, v);
    }
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
    }
    dfs1(1, 0);
    dfs2(1, 1);
    build(1, n, 1);
    cin >> m;
    for (int i = 1, u, v; i <= m; ++i) {
        cin >> op >> u >> v;
        if (op == "QMAX") {
            cout << qmax(u, v) << endl;
            
        } else if (op == "CHANGE") {
            update(id[u], v, 1, n, 1);
            
        } else if (op == "QSUM") {
            cout << qsum(u, v) << endl;
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值