题意
给定n个定,m条边,接下来有k组询问,每次给定一个集合,问能否用这些集合的点构成MST。
分析
如果每次只问一条边,我们可以用树剖或树上倍增找到这条链上最大值再替换成询问的边,但现在询问为一个集合,因此问题复杂很多。
关于kruskal有两个很有趣的定理
- 定义wi为每一条边,对于任意wi,我们选择长度<wi的边构成的联通块是相同的
- 定义最小生成树中边长为wi的边条数有ci条,对于所有的最小生成树来说,wi和ci都是相同的
考虑离线的处理方法,我们把所有询问的边按照权值进行分类,这样就可以做到在处理长度为wi的边时,长度<wi的边都已经处理完成了,如果我们当时加入权值为wi的边时发现构成环,说明这条边是不必要的,因此可以直接标记这个询问为false。但我们发现,相同权值的边处在不同的询问当中,因此我们在处理完一个询问的时候要撤销之前的影响,用可撤销并查集就可以解决。
AC Code
#include <bits/stdc++.h>
//#define ACM_LOCAL
#define fi first
#define se second
#define pb push_back
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 5e5 + 10, M = 5e5 + 10, INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
int n, m, k, cnt;
struct Edge {
int u, v, w;
bool operator < (const Edge &rhs) const {
return w < rhs.w;
}
}e[N];
struct Query {
int u, v, id;
bool operator < (const Query &rhs) const {
return id < rhs.id;
}
};
bool ans[N];
vector<Query> vec[N];
struct Undo_dsu {
stack<PII> st;
int fa[N], siz[N];
void init() {
while (st.size()) st.pop();
for (int i = 1; i <= n; i++) fa[i] = i, siz[i] = 1;
}
int find(int x) {
return fa[x] == x ? x : find(fa[x]);}
bool merge(int x, int y) {
int fx = find(x), fy = find(y);
if (fx == fy) return false;
if (siz[fx] > siz[fy]) swap(fx, fy), swap(x, y);
siz[fy] += siz[fx], fa[fx] = fy;
st.push({
fx, fy});
return true;
}
void undo() {
PII now = st.top();
fa[now.fi] = now.fi;
siz[now.se] -= siz[now.fi];
st.pop();
}
}dsu;
void solve() {
cin >> n >> m; dsu.init();
for (int i = 1; i <= m; i++) cin >> e[i].u >> e[i].v >> e[i].w;
cin >> k; for (int i = 1; i <= k; i++) {
int num; cin >> num;
while (num--) {
int x; cin >> x;
vec[e[x].w].push_back({
e[x].u, e[x].v, i});
}
}
for (int i = 1; i <= N-10; i++) if(vec[i].size()) sort(vec[i].begin(), vec[i].end());
for (int i = 1; i <= k; i++) ans[i] = true;
sort(e + 1, e + m + 1);
for (int i = 1; i <= m; ) {
int val = e[i].w, now = dsu.st.size();
for (int j = 0; j < vec[val].size(); j++) {
if (!ans[vec[val][j].id]) continue;
if (j && vec[val][j].id != vec[val][j-1].id) {
while (dsu.st.size() > now) dsu.undo();
}
if (!dsu.merge(vec[val][j].u, vec[val][j].v)) ans[vec[val][j].id] = false;
}
while (e[i].w == val) dsu.merge(e[i].u, e[i].v), i++;
}
for (int i = 1; i <= k; i++) printf("%s\n", ans[i] ? "YES" : "NO");
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
#ifdef ACM_LOCAL
freopen("input", "r", stdin);
freopen("output", "w", stdout);
#endif
solve();
return 0;
}