[luoguP11324] Speaker

题意

原题链接
给定一个带权无根树,第 i 个节点上有一个数 ci,每次询问给定两个点 x,y,在无根树上任选一点 z,使 cx+cy+czdist(x,z)dist(z,y) 最大,输出最大的值。

sol

考虑 z 可能有两种情况,要么是 xy 的路径上的一点 t,要么从路径上的一点 t 向外拐出来。无论怎样,最终结果都是 cx+cydist(x,y)+maxit相连{ct2dist(t,i)},我们可以通过换根 DP 来计算最后一项,因此问题就转化为了树上路径求最大值问题,使用树剖 + RMQ 即可。
注意可能会爆 int。

代码

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;
typedef long long LL;

const int N = 200005, M = N * 2;

int h[N], e[M], w[M], ne[M], idx;
int n, q;
int c[N];
int fid[N], gid[N];
LL f[N], sf[N], g[N], sg[N];
LL sum[N];
int fa[N], dfn[N], rk[N], top[N], hson[N], sz[N], dep[N], timestamp;
int Log2[N];
LL st[N][20];

void add(int a, int b, int c){
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}

void dp1(int u, int father){
    f[u] = c[u], fid[u] = 0;
    for (int i = h[u]; ~i; i = ne[i]){
        int j = e[i];
        if (j == father) continue;
        dp1(j, u);
        if (f[j] - 2 * w[i] >= f[u]) fid[u] = j, sf[u] = f[u], f[u] = f[j] - 2 * w[i];
        else if (f[j] - 2 >= sf[u]) sf[u] = f[j] - 2 * w[i];
    }
}

void dp2(int u, int father, int val){
    if (u == 1) g[u] = f[u], sg[u] = sf[u], gid[u] = fid[u];
    else {
        int fans;
        if (gid[father] == u) fans = max(sf[father], g[father]);
        else fans = max(f[father], g[father]);

        g[u] = f[u], sg[u] = sf[u], gid[u] = fid[u];

        if (fans - 2 * val >= g[u]) gid[u] = father, sg[u] = g[u], g[u] = fans - 2 * val;
        else if (fans - 2 * val >= sg[u]) sg[u] = fans - 2 * val;
    }

    for (int i = h[u]; ~i; i = ne[i]){
        int j = e[i];
        if (j == father) continue;
        dp2(j, u, w[i]);
    }
}

void init(int u, int father){
    fa[u] = father, dep[u] = dep[father] + 1, sz[u] = 1, hson[u] = 0;
    for (int i = h[u]; ~i; i = ne[i]){
        int j = e[i];
        if (j == father) continue;
        sum[j] = sum[u] + w[i];
        init(j, u);
        sz[u] += sz[j];
        if (sz[j] > sz[hson[u]]) hson[u] = j;
    }
}

void init2(int u, int father, int tp){
    dfn[u] = ++ timestamp, rk[timestamp] = u, top[u] = tp;
    if (!hson[u]) return ;
    init2(hson[u], u, tp);
    for (int i = h[u]; ~i; i = ne[i]){
        int j = e[i];
        if (j == father || j == hson[u]) continue;
        init2(j, u, j);
    }
}

void rmq_init(){
    Log2[0] = Log2[1] = 0;
    for (int i = 2; i <= n; i ++ ) Log2[i] = Log2[i / 2] + 1;
    
    for (int i = 1; i <= n; i ++ ) st[i][0] = g[rk[i]];
    for (int k = 1; k <= 17; k ++ ) 
        for (int i = 1; i + (1 << k) - 1 <= n; i ++ )
            st[i][k] = max(st[i][k - 1], st[i + (1 << k - 1)][k - 1]);
}

LL rmq_query(int l, int r){
    int lg = Log2[r - l + 1];
    return max(st[l][lg], st[r - (1 << lg) + 1][lg]);
}

int lca(int x, int y){
    while (top[x] != top[y]){
        if (dep[top[x]] < dep[top[y]]) swap(x, y);
        x = fa[top[x]];
    }
    if (dep[x] > dep[y]) swap(x, y);
    return x;
}

LL path_sum(int x, int y){
    int l = lca(x, y);
    return sum[x] - sum[l] + sum[y] - sum[l];
}

LL path_max(int x, int y){
    LL res = -1;
    while (top[x] != top[y]){
        if (dep[top[x]] < dep[top[y]]) swap(x, y);
        res = max(res, rmq_query(dfn[top[x]], dfn[x]));
        x = fa[top[x]];
    }
    if (dep[x] > dep[y]) swap(x, y);
    res = max(res, rmq_query(dfn[x], dfn[y]));
    return res;
}

void INIT(){
    dp1(1, 0);
    dp2(1, 0, 0);

    init(1, 0);
    init2(1, 0, 1);
    rmq_init();
}

int main(){
    memset(h, -1, sizeof h);

    scanf("%d%d", &n, &q);
    for (int i = 1; i <= n; i ++ ) scanf("%d", &c[i]);

    for (int i = 1; i < n; i ++ ) {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        add(a, b, c), add(b, a, c);
    }

    INIT();

    while (q -- ){
        int x, y;
        scanf("%d%d", &x, &y);
        LL ans = path_max(x, y) - path_sum(x, y) + c[x] + c[y];
        printf("%lld\n", ans);
    }

    return 0;
}
posted @   是一只小蒟蒻呀  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示