[luoguP11324] Speaker
题意
原题链接
给定一个带权无根树,第 个节点上有一个数 ,每次询问给定两个点 ,在无根树上任选一点 ,使 最大,输出最大的值。
sol
考虑 可能有两种情况,要么是 的路径上的一点 ,要么从路径上的一点 向外拐出来。无论怎样,最终结果都是 ,我们可以通过换根 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;
}
分类:
题解 / 2024赛时
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现