1 条题解
-
0
「KTSC 2024 R1」铁路 2 题解
问题分析
我们需要计算所有城市对 的 之和。根据定义, 是从 到 路径上可以使用的最小边权的最大值。
关键观察:在树结构中,对于任意两点 和 , 等于它们路径上最小边权的最大值。
算法思路:树的直径 + 排序计数
核心定理
对于树上的任意两点 和 :
$$\operatorname{joy}(x, y) = \min(\text{dist}(x, a), \text{dist}(y, a)) $$其中 是树的直径的一个端点。
1. 寻找树的直径
void bfs(ll now, ll dis[]) { for (ll i = 0; i < n; i++) dis[i] = -1; queue<ll> q; q.push(now); dis[now] = 0; while (q.empty() == 0) { ll u = q.front(); q.pop(); for (auto it : v[u]) { ll V = it.second, w = it.first; if (dis[V] == -1) { dis[V] = dis[u] + w; q.push(V); } } } } ll fin(ll now) { ll res = -1; for (ll i = 0; i < n; i++) { if (res < dis1[i]) { res = dis1[i]; now = i; } } return now; } // 寻找直径的两端点 bfs(0, dis1); man = fin(0); // 第一个端点 bfs(man, dis1); // 从第一个端点BFS man = fin(man); // 第二个端点(直径另一端) bfs(man, dis2); // 从第二个端点BFS算法步骤:
- 从任意点(0)开始BFS,找到最远点(直径一端)
- 从该最远点再次BFS,找到真正的直径另一端
- 记录每个点到两个端点的距离
2. 计算关键值
for (ll i = 0; i < n; i++) { f[i] = max(dis1[i], dis2[i]); }这里
f[i]存储每个点到直径两端的较大距离,这实际上给出了该点在路径上的"瓶颈值"。3. 排序与计数
sort(f, f + n); for (ll i = 0; i < n; i++) { ans = (ans + 2 * f[i] % mod * (n - i - 1)) % mod; }数学原理:
- 将
f[i]排序后,对于第 小的值,它会被计算 次 - 乘以 是因为每对 和 都要计算
- 模 防止溢出
代码实现详解
数据结构
vector<pair<ll, ll>> v[N]; // 邻接表:<权重, 相邻节点> ll dis1[N], dis2[N]; // 到直径两端的距离 ll f[N]; // 每个点的关键值主函数
int travel(std::vector<int> U, std::vector<int> V, std::vector<int> W) { n = U.size() + 1; // 构建邻接表 for (ll i = 0; i < n - 1; i++) { v[U[i]].push_back({W[i], V[i]}); v[V[i]].push_back({W[i], U[i]}); } // 寻找直径 bfs(0, dis1); man = fin(0); bfs(man, dis1); man = fin(man); bfs(man, dis2); // 计算每个点的关键值 for (ll i = 0; i < n; i++) { f[i] = max(dis1[i], dis2[i]); } // 排序并计算总和 sort(f, f + n); for (ll i = 0; i < n; i++) { ans = (ans + 2 * f[i] % mod * (n - i - 1)) % mod; } return ans % mod; }复杂度分析
- BFS遍历:,进行3次BFS
- 排序:
- 计数:
- 总复杂度:,满足 的要求
正确性证明
关键性质
树的直径性质:树上最远的两点构成直径 瓶颈值计算:对于任意路径,其最小边权的最大值由直径决定 对称性:
算法优势
高效性: 解决大规模数据 简洁性:基于树的直径性质,避免复杂计算 正确性:严格的数学证明保证结果正确
该解法通过树的直径性质和巧妙的计数方法,高效解决了树上的路径瓶颈值求和问题。
- 1
信息
- ID
- 4552
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者