C. Envy
C. Envy
For a connected undirected weighted graph $G$, MST (minimum spanning tree) is a subgraph of $G$ that contains all of $G$'s vertices, is a tree, and sum of its edges is minimum possible.
You are given a graph $G$. If you run a MST algorithm on graph it would give you only one MST and it causes other edges to become jealous. You are given some queries, each query contains a set of edges of graph $G$, and you should determine whether there is a MST containing all these edges or not.
Input
The first line contains two integers $n, m$ $(2 \leq n, m \leq 5 \cdot 10^5, n - 1 \leq m)$ — the number of vertices and edges in the graph and the number of queries.
The $i$-th of the next $m$ lines contains three integers $u_i$, $v_i$, $w_i$ $(u_i \ne v_i, 1 \leq w_i \leq 5 \cdot 10^5)$ — the endpoints and weight of the $i$-th edge. There can be more than one edges between two vertices. It's guaranteed that the given graph is connected.
The next line contains a single integer $q$ $(1 \leq q \leq 5 \cdot 10^5)$ — the number of queries.
$q$ lines follow, the $i$-th of them contains the $i$-th query. It starts with an integer $k_i$ $(1 \leq k_i \leq n - 1)$ — the size of edges subset and continues with $k_i$ distinct space-separated integers from $1$ to $m$ — the indices of the edges. It is guaranteed that the sum of $k_i$ for $1 \leq i \leq q$ does not exceed $5 \cdot 10^5$.
Output
For each query you should print "YES" (without quotes) if there's a MST containing these edges and "NO" (of course without quotes again) otherwise.
Example
Input
5 7
1 2 2
1 3 2
2 3 1
2 4 1
3 4 1
3 5 2
4 5 2
4
2 3 4
3 3 4 5
2 1 7
2 1 2
Output
YES
NO
YES
NO
Note
This is the graph of sample:

Weight of minimum spanning tree on this graph is $6$.
MST with edges $(1, 3, 4, 6)$, contains all of edges from the first query, so answer on the first query is "YES".
Edges from the second query form a cycle of length $3$, so there is no spanning tree including these three edges. Thus, answer is "NO".
解题思路
在讲解本题做法之前,首先证明一个性质。
性质:对于同一个无向图,在 Kruskal 算法执行过程中,当处理完所有权值小于某个值的边后,图的连通分量结构(各连通分量的节点组成)是唯一的,与处理同权值边的顺序无关。
证明:采用数学归纳法,对边权值 $w$ 进行归纳。当 $w = 0$ 时,初始状态为每个节点均为独立的连通分量,此时尚未加入任何边,显然成立。假设在处理完所有边权小于 $w$ 的边后,图的连通分量结构唯一确定。现在我们证明处理完边权等于 $w$ 的边后,连通分量结构仍唯一确定。
设当前已处理完边权小于 $w$ 的边,得到连通分量集合 $\mathcal{C} = \{C_1, C_2, \ldots, C_m \}$。令 $E_w$ 为所有权值等于 $w$ 的边构成的集合,根据 Kruskal 算法需从中选出能连接不同连通分量的边,对应的边集为 $E_w'=\{ (u,v,w) \in E_w \mid \text{端点 } u,v \text{ 属于 } \mathcal{C} \text{ 中不同的连通分量} \}$。$E_w'$ 中的边可看作以 $\mathcal{C}$ 为点集的图 $H$ 中的边,其中每条边连接两个不同节点(连通分量)。根据 Kruskal 算法,选择的边需构成 $H$ 的最大生成森林(即无环且最大化边数)。
若 $E_w'$ 中无边形成环,此时 $H$ 仍为森林,按任意顺序加入 $E_w'$ 中的边都不会形成环,最终连通分量由 $H$ 的连通性决定,与处理顺序无关。
若 $E_w'$ 中有边形成环,设按照某顺序处理时,某条边 $(u,v,w)$ 因形成环未被选中(即 $u,v$ 已连通),此时必存在一条由已加入的 $E_w'$ 中的边构成连接 $u$ 和 $v$ 的路径 $P$。若将 $(u,v,w)$ 替换为 $P$ 中任意边 $(u',v',w') \in P \cap E_w'$,此时 $H$ 仍为最大生成森林,且 $\mathcal{C}$ 中合并后的连通分量结构保持不变。因此,在不同的处理顺序下,虽然从 $E_w'$ 中选择的边可能不同,但均可通过该替换,最终的连通分量结构是唯一的。
因此处理完边权等于 $w$ 的边后,连通分量结构仍唯一确定。得证。
接下来,考虑只有一组询问的情况,判断这组边是否出现在同一棵最小生成树中。按照边权对边进行升序排序,并同时处理所有边权相同的边。在枚举到边权为 $w$ 的边时,确保已经按照 Kruskal 算法处理了原图中所有边权小于 $w$ 的边,然后依次处理边权为 $w$ 的边,并尝试将其加入图中。根据上述性质,处理顺序不重要,并且如果某条边无法加入,则说明该组边不会出现在同一棵最小生成树中。在处理完边权为 $w$ 的所有边后,需要从图中删除刚刚加入的询问的边,以免影响后续边的判断。
接着考虑如何处理多组询问。显然如果对每组询问单独处理,时间复杂度为 $O(\sum{k_i \log{k_i}} + m \sum{k_i})$。因此可以考虑离线处理,合并处理边权相同的边。具体而言,我们先对每组询问的边按边权分组,然后同时处理所有询问中边权相同的分组。与上面的方法类似,在处理边权为 $w$ 的边时,先确保已经按照 Kruskal 算法处理了原图中所有边权小于 $w$ 的边,然后依次处理同一批询问的边。在处理完成后,需要从图中删除刚刚加入的边,然后再处理下一组询问的边。
由于涉及删除最近加入的边,可以用可撤销并查集代替普通并查集,以实现线性删除。
AC 代码如下,时间复杂度为 $O(m (\log{n} + \log{m}) + W + \sum{k_i} \log{n})$:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 5e5 + 5;
array<int, 3> e[N];
vector<array<int, 3>> g[N];
int fa[N], s[N], tp;
array<int, 2> stk[N];
bool ans[N];
int find(int x) {
return fa[x] == x ? fa[x] : find(fa[x]);
}
bool merge(int x, int y) {
x = find(x), y = find(y);
if (x == y) return false;
if (s[x] > s[y]) swap(x, y);
s[y] += s[x];
fa[x] = y;
stk[++tp] = {x, y};
return true;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m, k;
cin >> n >> m;
for (int i = 1; i <= m; i++) {
cin >> e[i][0] >> e[i][1] >> e[i][2];
}
cin >> k;
for (int i = 0; i < k; i++) {
int c;
cin >> c;
while (c--) {
int x;
cin >> x;
g[e[x][2]].push_back({e[x][0], e[x][1], i});
}
}
sort(e + 1, e + m + 1, [&](array<int, 3> &a, array<int, 3> &b) {
return a[2] < b[2];
});
iota(fa + 1, fa + n + 1, 1);
fill(s + 1, s + n + 1, 1);
for (int i = 1, j = 1; i < N; i++) {
while (j <= m && e[j][2] < i) {
merge(e[j][0], e[j][1]);
j++;
}
int l = 0;
while (l < g[i].size()) {
int r = l + 1, c = 0;
while (r < g[i].size() && g[i][l][2] == g[i][r][2]) {
r++;
}
while (l < r) {
if (merge(g[i][l][0], g[i][l][1])) c++;
else ans[g[i][l][2]] = true;
l++;
}
while (c--) {
auto p = stk[tp--];
s[p[1]] -= s[p[0]];
fa[p[0]] = p[0];
}
}
}
for (int i = 0; i < k; i++) {
cout << (ans[i] ? "NO" : "YES") << '\n';
}
return 0;
}
参考资料
题解 CF891C 【Envy】 - 洛谷专栏:https://www.luogu.com.cn/article/ok5ea1ws
CF891C题解 - 洛谷专栏:https://www.luogu.com.cn/article/6uy5dohc
最小生成树相关性质 - cbdsopa - 博客园:https://www.cnblogs.com/cbdsopa/p/16035326.html
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/18878722

浙公网安备 33010602011771号