题目大意 :给出有\(n\)个点\(m\)条边的图,接下来进行若干次操作,每次操作取出当前图的最小生成树,然后删去这些构成最小生成树的边,知道该图不连通,输出每条边在第几次操作时被删除
思路 :由于构成最小生成树的边数是\(n-1\)条边,所以最多操作次数为\(\lfloor \frac{m}{n-1} \rfloor\),每次操作都会有一个并查集(kruskal)所以创建\(\lfloor \frac{m}{n-1} \rfloor\)个并查集,从小到大遍历所有的边,看看属于哪个并查集,即在第几次被删除,找到的第一个不连通的并查集即为该边所在的并查集,找到后将这个边插入该并查集,即将\(u\),\(v\)合并(同祖先),并将该并查集操作次数+1。操作完后访问所有的边,判断其所在并查集是否操作了\(n-1\)次,如果为否输出-1,否侧输出该边属于第几个并查集(该边第几次删除)
#include <bits/stdc++.h>
const double eps = 1e-6;
using namespace std;
const int N = 1e5 + 10;
const int M = 5e4 + 10;
#define int long long
struct node {
int u, v, val;
};
void solve() {
int n, m;
cin >> n >> m;
vector<vector<int> > fa(m / (n - 1) + 1, vector<int>(n + 1));
vector<node> E(m + 1);
vector<int> ans(m + 1), cnt(m + 1);
function<int(int, int)> findf = [&](int x, int c) -> int {
if (fa[c][x] == x) return x;
else return fa[c][x] = findf(fa[c][x], c);
};
for (int j = 0; j <= (m / (n - 1)); j++) {
for (int i = 1; i <= n; i++) {
fa[j][i] = i;
//cout<<"fa[" << j <<"]["<<i << "]="<<fa[j][i]<<'\n';
}
}
//cout << fa[1][1] << '\n';
for (int i = 1; i <= m; i++) {
int u, v;
cin >> u >> v;
E[i] = {u, v, i};
}
for (int i = 1; i <= m; i++) {
auto [u, v, w] = E[i];
int l = 1, r = m / (n - 1);
int idx = -1;
while (l <= r) {
int mid = (l + r) >> 1;
//cout << findf(u, mid) << ' ' << findf(v, mid) << '\n';
if (findf(u, mid) == findf(v, mid)) {
l = mid + 1;
} else {
idx = mid;
r = mid - 1;
}
}
if (idx != -1) {
fa[idx][findf(u, idx)] = findf(v, idx);
cnt[idx]++;
}
ans[i] = idx;
}
for (int i = 1; i <= m; i++) {
if (ans[i] == -1) cout << -1;
else if (ans[i] == m / (n - 1) + 1 || cnt[ans[i]] != n - 1) cout << "-1";
else cout << ans[i];
cout << ' ';
}
cout << '\n';
}
signed main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int _ = 1;
cin >> _;
while (_--) solve();
}
标签:钉耙,vector,int,查集,第几次,2024,fa,cats,操作
From: https://www.cnblogs.com/yoez123/p/18358766