F - MST Query
F - MST Query
Problem Statement
You are given a weighted undirected connected graph with vertices and edges, where vertices are numbered to and edges are numbered to . Edge connects vertices and with a weight of .
You are given queries to process sequentially. The -th query is described as follows:
- You are given integers . Add an edge with weight between vertices and in . Then, print the sum of the weights of the edges in a minimum spanning tree of .
Constraints
- The graph is connected before processing the queries.
- All input values are integers.
Input
The input is given from Standard Input in the following format:
Output
Print lines. The -th line should contain the answer to the -th query.
Sample Input 1
4 4
1 2 6
2 3 5
2 4 4
1 3 3
1 2 3
1 4 10
3 4 1
Sample Output 1
12
10
10
7
Here is the graph after adding the edge for each query. The edges included in the minimum spanning tree are colored red.
Sample Input 2
8 6
1 8 8
1 6 10
1 5 8
2 6 6
6 7 6
1 3 9
2 4 7
1 3 4
1 6 7
3 4 6
1 5 1
7 8 4
3 5 3
Sample Output 2
49
46
45
38
34
33
解题思路
容易想到可以动态维护最小生成树,对于每个询问 ,当插入这条边时必然会形成一个环,为了使得生成树最小,则需要在树中找到 到 这条路径上最大边权对应的边,如果最大边权比 大则删除这条边,再插入边 。LCT 可以实现上述的操作,但我没学过不会。
注意到边权非常小,我们可以换一种思路去思考这个问题。上面的做法是动态维护大小为 的边集,而暴力的做法是直接往边集里加新边,每次跑一遍 Kruskal 求 MST。回想 Kruskal 算法,本质是按边权从小到大把不会构成环的边加到边集中,最后得到的 MST 的权重就是 ,其中 表示边集中边权 的数量。现在边权最大只有 ,显然可以直接去暴力计算这个结果,但问题是如何知道 。
每往边集加入一条边后,连通块的数量就会减 。考虑把不构成环的权值小于 的边都加入后的连通块数量,记为 ,再把不构成环的权值为 的边加入后的连通块数量,记为 ,那么边集中权值为 的边的数量就是 。
为此我们需要维护 个并查集,分别维护加入权值不超过 且不构成环的边所构成的连通块。每要次插入边 时,因此遍历 所对应的并查集,如果 和 不在同一个连通块内,则进行合并,对应并查集的连通块数量减 。
AC 代码如下,时间复杂度为 :
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 5, M = 15;
int fa[M][N], cnt[M];
int find(int *fa, int x) {
return fa[x] == x ? fa[x] : fa[x] = find(fa, fa[x]);
}
int main() {
int n, m;
scanf("%d %d", &n, &m);
for (int i = 0; i <= 10; i++) {
iota(fa[i], fa[i] + n + 1, 0);
cnt[i] = n;
}
for (int i = 0; i < n - 1; i++) {
int u, v, w;
scanf("%d %d %d", &u, &v, &w);
for (int j = w; j <= 10; j++) {
int a = find(fa[j], u), b = find(fa[j], v);
if (a != b) {
fa[j][a] = b;
cnt[j]--;
}
}
}
while (m--) {
int u, v, w;
scanf("%d %d %d", &u, &v, &w);
for (int j = w; j <= 10; j++) {
int a = find(fa[j], u), b = find(fa[j], v);
if (a != b) {
fa[j][a] = b;
cnt[j]--;
}
}
int ret = 0;
for (int i = 1; i <= 10; i++) {
ret += (cnt[i - 1] - cnt[i]) * i;
}
printf("%d\n", ret);
}
return 0;
}
参考资料
Editorial - Tokio Marine & Nichido Fire Insurance Programming Contest 2024(AtCoder Beginner Contest 355):https://atcoder.jp/contests/abc355/editorial/10072
AtCoder Beginner Contest 355 - ~Lanly~ - 博客园:https://www.cnblogs.com/Lanly/p/18213969#f---mst-query-abc355-f
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/18216552
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
2023-05-27 股票买卖 V
2021-05-27 公路村村通
2021-05-27 旅游规划
2021-05-27 哈利·波特的考试
2021-05-27 列出连通集
2021-05-27 堆中的路径
2021-05-27 Root of AVL Tree