F - MST Query
F - MST Query
Problem Statement
You are given a weighted undirected connected graph $G$ with $N$ vertices and $N-1$ edges, where vertices are numbered $1$ to $N$ and edges are numbered $1$ to $N-1$. Edge $i$ connects vertices $a_i$ and $b_i$ with a weight of $c_i$.
You are given $Q$ queries to process sequentially. The $i$-th query is described as follows:
- You are given integers $u_i, v_i, w_i$. Add an edge with weight $w_i$ between vertices $u_i$ and $v_i$ in $G$. Then, print the sum of the weights of the edges in a minimum spanning tree of $G$.
Constraints
- $2 \leq N \leq 2 \times 10^5$
- $1 \leq Q \leq 2 \times 10^5$
- $1 \leq a_i < b_i \leq N$
- $1 \leq u_i < v_i \leq N$
- $1 \leq c_i, w_i \leq 10$
- 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:
$N$ $Q$
$a_1$ $b_1$ $c_1$
$\vdots$
$a_{N-1}$ $b_{N-1}$ $c_{N-1}$
$u_1$ $v_1$ $w_1$
$\vdots$
$u_Q$ $v_Q$ $w_Q$
Output
Print $Q$ lines. The $i$-th line should contain the answer to the $i$-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
解题思路
容易想到可以动态维护最小生成树,对于每个询问 $(u,v,w)$,当插入这条边时必然会形成一个环,为了使得生成树最小,则需要在树中找到 $u$ 到 $v$ 这条路径上最大边权对应的边,如果最大边权比 $w$ 大则删除这条边,再插入边 $(u,v)$。LCT 可以实现上述的操作,但我没学过不会。
注意到边权非常小,我们可以换一种思路去思考这个问题。上面的做法是动态维护大小为 $n-1$ 的边集,而暴力的做法是直接往边集里加新边,每次跑一遍 Kruskal 求 MST。回想 Kruskal 算法,本质是按边权从小到大把不会构成环的边加到边集中,最后得到的 MST 的权重就是 $\sum{i \cdot c_i}$,其中 $c_i$ 表示边集中边权 $i$ 的数量。现在边权最大只有 $10$,显然可以直接去暴力计算这个结果,但问题是如何知道 $c_i$。
每往边集加入一条边后,连通块的数量就会减 $1$。考虑把不构成环的权值小于 $i$ 的边都加入后的连通块数量,记为 $\text{cnt}[i-1]$,再把不构成环的权值为 $i$ 的边加入后的连通块数量,记为 $\text{cnt}[i]$,那么边集中权值为 $i$ 的边的数量就是 $c_i = \text{cnt}[i-1] - \text{cnt}[i]$。
为此我们需要维护 $10$ 个并查集,分别维护加入权值不超过 $i$ 且不构成环的边所构成的连通块。每要次插入边 $(u,v,w)$ 时,因此遍历 $w \sim 10$ 所对应的并查集,如果 $u$ 和 $v$ 不在同一个连通块内,则进行合并,对应并查集的连通块数量减 $1$。
AC 代码如下,时间复杂度为 $O((n+m) \cdot W)$:
#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