Edges in MST

传送门


思路:

首先我们得明白 Kruskal 的操作过程:对最小的边进行贪心

那我们就有一个想法,对于一条在 MST 中的边 \(e\) ,只有与它权值相同的边才有可能去替换它

那我们就统计图中某个权值 \(val\) 的边数 \(cnt\),再统计实际选入 MST 里的权值为 \(val\) 的边数 \(ch\)

显然,如果 \(cnt==ch\) ,那么这种边权的边一定都要选;如果 \(ch==0\) ,代表这种边权的边一定都不用选

那万一这两种情况都不是,是不是就是可能选了?

显然不是的,例如下图:

假设边权都是 1 ,如果我们单看左边那个环,确实是都是 可能选中;但再看环外伸出的那条边,如果不选它,我们就连生成树都做不出来了

因此我们需要换个思路


正解:

显然,对于小于当前边权值 \(val\) 的边,它们之中无论选那些边,对于我决策当前边是等价的:即连通块的情况相同

所以我们就考虑将相同的边放在一起决策

对于一条连接 \(u,v\) 的边,如果 \(u,v\) 已经在同一个连通块内,显然这条边是一定不用选的,因为前面边权更小的边一定更优,我们就不再继续考虑这些边

而对于连接不同连通块的边,它有可能是 必选项,也可能是 有可能选项

到这里我们不难看出,如果不选择某一条边,会导致两个连通块无法连通(仅限于考虑权值相同的边的情况下),那它就是必选项

换而言之,我们将连通块都缩成一个点,并加入权值相同的边,如果某条边是 桥(割边),那么它就是必选项

最后剩下的就是可能选的边


总结:

  • 每次考虑权值相同的边

  • 对于 \(u,v\) 已在同一个连通块的边,我们剔除它,不再继续考虑

  • 将连通块缩点,加入剩下的边,跑一遍 tarjan桥(割边)

  • 再正常跑最小生成树的步骤,得到新的连通块


代码:

#include<iostream>
#include<fstream>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#define LL long long
inline int reads()
{
    int sign = 1, re = 0; char c = getchar();
    while(c < '0' || c > '9'){if(c == '-') sign = -1; c = getchar();}
    while('0' <= c && c <= '9'){re = re * 10 + (c - '0'); c = getchar();}
    return sign * re;
}
int n, m, fa[100005];
struct Node
{
    int u, v, w, id, ans;
}r[100005];
inline bool cmp(Node a, Node b) {return a.w < b.w;}
inline bool cmpback(Node a, Node b) {return a.id < b.id;}
int finds(int now)
{
    if(now == fa[now]) return now;
    return fa[now] = finds(fa[now]);
}
inline bool check(int u, int v)
{
    u = finds(u), v = finds(v);
    if(u == v) return true;
    else return false;
}
inline void match(int x, int y)
{
    x = finds(x), y = finds(y);
    if(x != y) fa[y] = x;
}
std::vector<std::pair<int, int> > eg[100005];
int dfn[100005], low[100005], dcnt;
void tarjan(int now, int la)
{
    dfn[now] = low[now] = ++dcnt;
    for(int i = 0; i < eg[now].size(); i++)
    {
        int to = eg[now][i].first;
        if(eg[now][i].second == la) continue;
        if(!dfn[to]) tarjan(to, eg[now][i].second), low[now] = std::min(low[now], low[to]);
        else low[now] = std::min(low[now], dfn[to]);
        if(low[to] > dfn[now]) r[eg[now][i].second].ans = 1;
    }
}
signed main()
{
#ifndef ONLINE_JUDGE
	freopen("test.in", "r", stdin);
	freopen("test.out", "w", stdout);
#endif
    n = reads(), m = reads();
    for(int i = 1; i <= m; i++)
    {
        int u = reads(), v = reads(), w = reads();
        r[i] = (Node){u, v, w, i, 0};
    }
    std::sort(r + 1, r + 1 + m, cmp);
    for(int i = 1; i <= n; i++) fa[i] = i;
    for(int i = 1; i <= m; i++)
    {
        int j = i;
        while(r[j + 1].w == r[i].w) j++;
        for(int k = i; k <= j; k++)
            if(check(r[k].u, r[k].v)) r[k].ans = -1;
            else eg[fa[r[k].u]].push_back(std::make_pair(fa[r[k].v], k)), eg[fa[r[k].v]].push_back(std::make_pair(fa[r[k].u], k));
        for(int k = i; k <= j; k++)
            if(r[k].ans != -1 && !dfn[fa[r[k].u]])
            {
                dcnt = 0;
                tarjan(fa[r[k].u], 0);
            }
        for(int k = i; k <= j; k++)
            eg[fa[r[k].u]].clear(), eg[fa[r[k].v]].clear(), low[fa[r[k].u]] = low[fa[r[k].v]] = dfn[fa[r[k].u]] = dfn[fa[r[k].v]] = 0;
        for(int k = i; k <= j; k++)
            match(r[k].u, r[k].v);
        i = j;
    }
    std::sort(r + 1, r + 1 + m, cmpback);
    for(int i = 1; i <= m; i++)
        if(r[i].ans == -1) printf("none\n");
        else if(r[i].ans == 1) printf("any\n");
        else printf("at least one\n");
    return 0;
}
posted @ 2022-03-11 21:28  zuytong  阅读(30)  评论(0编辑  收藏  举报