第46届ICPC澳门站 K - Link-Cut Tree // 贪心 + 并查集 + DFS


原题链接:K-Link-Cut Tree_第46屆ICPC 東亞洲區域賽(澳門)(正式賽) (nowcoder.com)

 

题意:

要求一个边权值总和最小的环,并从小到大输出边权值(2的次幂);若不存在环,输出-1。

 

思路:

考虑按权值从小到大加边,当出现环时(利用并查集判环),这个环必定是总权值最小的环。

找到环后,不再加边,并从环上某一点做DFS,找到从该点出发并再次回到该点的环。

 

代码:

#include 
#define PII pair
#define fi first
#define se second
using namespace std;

const int N = 100010;

int n, m, fa[N], stk[N], top;
vector v[N];
bool vis[N], mark;

int find(int x)
{
    return fa[x] == x ? x : fa[x] = find(fa[x]);
}

void dfs(int u, int pre, int ed)
{
    if(mark) return;
    if(u == ed && ~pre) {
        mark = true;
        sort(stk + 1, stk + 1 + top);
        for(int i = 1; i <= top; i++) printf("%d ", stk[i]);
        cout << endl;
    }
    for(auto i : v[u]) {
        if(vis[i.fi] || i.fi == pre) continue;
        stk[++top] = i.se, vis[i.fi] = true;
        dfs(i.fi, u, ed);
        -- top, vis[i.fi] = false;
    }
}

void solve()
{
    cin >> n >> m;
    for(int i = 1; i <= n; i++) {
        fa[i] = i, vis[i] = false;
        v[i].clear();
    }
    int st = 0;
    bool flag = false;  // 是否存在环
    for(int i = 1; i <= m; i++) {
        int x, y;
        scanf("%d%d", &x, &y);
        if(flag) continue;
        v[x].push_back({y, i}), v[y].push_back({x, i});
        int fx = find(x), fy = find(y);
        if(fx == fy) flag = true, st = x;
        else fa[fx] = fy;
    }
    if(!flag) puts("-1");
    else mark = false, dfs(st, -1, st);
}

int main()
{
    int test;
    cin >> test;
    while(test--) solve();

    return 0;
}