题解:P6419 [COCI2014-2015#1] Kamp
P6419 [COCI2014-2015#1] Kamp
本题大部分人都是换根
题意
给定一棵
前置知识
如果你做过 [SDOI2015] 寻宝游戏,或者说你学过虚树,你就会知道有这样一个重要的性质:
在一棵树上有
特别的,规定
证明其实很简单。回顾
而在
问题分析
认真思考原问题,不难发现节点
推导是显然的,原问题就是一个
约定
考虑先来求
我们可以先求关键点组成最小联通子树,然后考虑插入一个点
规定点集以
则可以轻松地用
实现时,可以二分点集,也可以直接枚举
接下来,我们要求
一开始我思维太僵化,没想出来怎么求,故发 讨论版 讨论做法。
@小粉兔 提出了一个简单的做法,在这里拜谢粉兔:
然后,$O(1)$ 求树上两点距离 / LCA 是熟知的。
当然,利用
代码
轻度压行,不过写的还算清晰。
#include <bits/extc++.h>
#define inline __always_inline
template <typename T> inline void read(T &x)
{
char ch;
for (ch = getchar(); !isdigit(ch); ch = getchar());
for (x = 0; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
}
const int MaxN = 5e5 + 5, MaxK = MaxN;
struct edge_t { int v, w; };
std::vector<edge_t> graph[MaxN];
inline void add_edge(int u, int v, int w) { graph[u].push_back({v, w}), graph[v].push_back({u, w}); }
int n, k, u, v, w, x[MaxN];
int parent[MaxN], head[MaxN], hson[MaxN];
int depth[MaxN], size[MaxN], dfn[MaxN], map[MaxN], cur = 1;
int64_t sum[MaxN], max[MaxN];
void build(int u)
{
size[u] = 1;
for (auto &&[v, w] : graph[u])
if (v != parent[u])
{
parent[v] = u;
depth[v] = depth[u] + 1;
sum[v] = sum[u] + w;
build(v);
size[u] += size[v];
if (size[hson[u]] < size[v]) hson[u] = v;
}
}
void decomposition(int u, int h)
{
map[dfn[u] = cur++] = u;
head[u] = h;
if (hson[u]) decomposition(hson[u], h);
for (auto &&[v, w] : graph[u])
if (v != parent[u] && v != hson[u])
decomposition(v, v);
}
int LCA(int u, int v)
{
while (head[u] != head[v])
{
if (depth[head[u]] < depth[head[v]]) std::swap(u, v);
u = parent[head[u]];
}
return depth[u] < depth[v] ? u : v;
}
inline int64_t dis(int u, int v)
{
int lca = LCA(u, v);
return sum[u] + sum[v] - sum[lca] * 2;
}
auto cmp = [](int x, int y) { return dfn[x] < dfn[y]; };
int64_t d[MaxN]; char key[MaxN];
int p, q;
void dfs(int u, int parent)
{
for (auto &&[v, w] : graph[u])
if (v != parent)
{
d[v] = d[u] + w;
if (key[v] && d[p] < d[v]) p = v;
dfs(v, u);
}
}
int64_t D[MaxN];
int main()
{
read(n), read(k);
for (int i = 1; i < n; i++)
read(u), read(v), read(w), add_edge(u, v, w);
build(1), decomposition(1, 1);
for (int i = 1; i <= k; i++) read(x[i]), key[x[i]] = 1;
dfs(x[1], 0), q = p, dfs(q, 0); // 寻找距离最远的两点。
for (int i = 1; i <= n; i++)
max[i] = std::max(dis(i, p), dis(i, q));
std::sort(x + 1, x + k + 1, cmp);
x[0] = x[k], x[k + 1] = x[1]; // 添加哨兵,简化边界处理
for (int i = 1; i <= k; i++) D[0] += dis(x[i], x[i + 1]);
for (int i = 1, id = 1; i <= n; i++)
{
if (id <= k && dfn[x[id]] < i) id++;
int u = map[i];
D[u] = D[0] - dis(x[id - 1], x[id]) + dis(x[id - 1], u) + dis(u, x[id]) - max[u];
}
for (int i = 1; i <= n; i++) printf("%ld\n", D[i]);
return 0;
}