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.
The first line contains two integers n, m (2 ≤ n, m ≤ 5·105, n - 1 ≤ 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 ui, vi, wi (ui ≠ vi, 1 ≤ wi ≤ 5·105) — 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 ≤ q ≤ 5·105) — the number of queries.
q lines follow, the i-th of them contains the i-th query. It starts with an integer ki (1 ≤ ki ≤ n - 1) — the size of edges subset and continues with ki distinct space-separated integers from 1 to m — the indices of the edges. It is guaranteed that the sum of ki for 1 ≤ i ≤ q does not exceed 5·105.
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.
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
YES NO YES NO
This is the graph of sample:
![](https://i-blog.csdnimg.cn/blog_migrate/122ceb07f9eb9ac23b9bee5cbaa2b683.png)
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算法的过程,在生成最小生成树的某一时刻会形成若干点集(树),它们内部都是以尽可能短的边相连的,我们称之为最小生成森林,设此时用上的边中最长的那条长度为x,那么如果此时用大于x的边连接同一点集的两个点必不可行。所以必须能够动态维护维护最小生成森林(在此学习了一波可撤销的并查集),所谓可撤销就是当新一轮的询问到来或研究更大的边权之时,把当前边的两端点复原至最小生成森林各自的子集中,这样就能判断是否会形成环,继而判断这组边是否可行。
【代码】
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int MAXN = 5e5 + 5;
struct edge {
int from, to, dist;
};
int n, m, q, cnt;
int fa_1[MAXN], fa_2[MAXN], kase[MAXN];
bool wa[MAXN];
edge e[MAXN];
vector<int> gra[MAXN];
vector<pair<int, int> > qr[MAXN];
int findfather_1(int x)
{
return (fa_1[x] == x ? x : fa_1[x] = findfather_1(fa_1[x]));
}
int findfather_2(int x)
{
if (kase[x] != cnt) {kase[x] = cnt; fa_2[x] = findfather_1(x);}
return (x == fa_2[x] ? x : fa_2[x] = findfather_2(fa_2[x]));
}
int main()
{
scanf("%d %d", &n, &m);
int a, b, c, mx = 0;
for (int i = 1; i <= m; i++) {
scanf("%d %d %d", &a, &b, &c);
e[i].from = a;
e[i].to = b;
e[i].dist = c;
gra[c].push_back(i);
mx = max(mx, c);
}
int k, x;
scanf("%d", &q);
for (int i = 1; i <= q; i++) {
scanf("%d", &k);
for (int j = 1; j <= k; j++) {
scanf("%d", &x);
qr[e[x].dist].push_back(make_pair(i, x));
}
}
memset(wa, false, sizeof(wa));
memset(kase, 0, sizeof(kase));
for (int i = 1; i <= n; i++) fa_1[i] = i;
cnt = 0;
for (int i = 1; i <= mx; i++) {
sort(qr[i].begin(), qr[i].end());
for (int j = 0; j < qr[i].size(); j++) {
if (j == 0 || qr[i][j].first != qr[i][j - 1].first) cnt++;
int x = findfather_2(e[qr[i][j].second].from);
int y = findfather_2(e[qr[i][j].second].to);
if (x == y) wa[qr[i][j].first] = true;
fa_2[y] = x;
}
for (int j = 0; j < gra[i].size(); j++) {
int x = findfather_1(e[gra[i][j]].from);
int y = findfather_1(e[gra[i][j]].to);
fa_1[y] = x;
}
}
for (int i = 1; i <= q; i++)
printf(!wa[i] ? "YES\n" : "NO\n");
return 0;
}