2024“钉耙编程”中国大学生算法设计超级联赛(8)1006 cats 的最小生成树

题目大意 :给出有\(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();  
}
posted @ 2024-08-14 13:22  yoez123  阅读(72)  评论(0)    收藏  举报