[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;
}

浙公网安备 33010602011771号