[luoguP11324] Speaker

题意

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

sol

考虑 \(z\) 可能有两种情况,要么是 \(x\to y\) 的路径上的一点 \(t\),要么从路径上的一点 \(t\) 向外拐出来。无论怎样,最终结果都是 \(c_x+c_y-dist(x,y)+\max_{i \text{与} t \text{相连}}\{c_t-2\cdot dist(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 @ 2024-11-24 21:53  是一只小蒟蒻呀  阅读(22)  评论(0)    收藏  举报