[Codeforces]160D - Edges in MST

本文介绍了一种算法,用于确定无向图中最小生成树中必定出现、可能出现及必定不会出现的边。通过排序和并查集+DFS求割边的方法,确保了找到的边符合最小生成树的要求。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Analysis

    这道题的大意是在一个无向图中,任意构造一棵最小生成树,求有哪一些边一定出现,哪一些边有可能出现,哪一些边一定不会出现。英语太渣了不敢在Codeforces上写题解(而且那里已经有很好的题解了),就在这里自己理一下思路吧。把所有边按权值由小到大排序,像Kruskal这样做过来,但是权值相同的边要一起做(有一个结论,设T是图G的一棵最小生成树,L是T中各边权值的一个已排序的列表,那么对于G的任何其他最小生成树T',L也是T'中各边权值的一个已排序的列表,参见《算法导论》P347练习23.1-8),那么在这些权值两两相同的边中属于最小生成树的边数是确定的。如果其中某一条边连接的是在添加这些边之前就已连通的点,那么这条边肯定不会存在于最小生成树中,如果添加了这些边后某一条边是割边,那么这条边一定存在于所有的最小生成树中。剩下的边都是有可能存在于最小生成树中。那么用并查集+dfs求割边即可。

Accepted Code

type
    edge=record
        a,b,w,lab:longint;
    end;
    edge2=record
        dt,lab,next:longint;
    end;

var
    e:array[0..100100] of edge;
    g:array[0..200200] of edge2;
    first,dfn,lowlink,ans,f,fa:array[0..100100] of longint;
    i,n,m,t1,t2,sign,now,last,num,edgenum:longint;

procedure sort(l,r:longint);
var
    i,j,mid:longint;
    tmp:edge;
begin
    i:=l;
    j:=r;
    mid:=e[(l+r+r) div 3].w;
    repeat
        while e[i].w<mid do
            inc(i);
        while mid<e[j].w do
            dec(j);
        if not (i>j) then
        begin
            tmp:=e[i];
            e[i]:=e[j];
            e[j]:=tmp;
            inc(i);
            dec(j);
        end;
    until i>j;
    if i<r then
        sort(i,r);
    if l<j then
        sort(l,j);
end;

procedure addedge(u,v,i:longint);
begin
    inc(edgenum);
    g[edgenum].dt:=v;
    g[edgenum].lab:=e[i].lab;
    g[edgenum].next:=first[u];
    first[u]:=edgenum
end;

procedure dfs(u:longint);
var
    v,t:longint;
begin
    inc(sign);
    dfn[u]:=sign;
    lowlink[u]:=sign;
    t:=first[u];
    while t<>0 do
    begin
        v:=g[t].dt;
        if g[t].lab<>f[u] then
        begin
            if dfn[v]=0 then
            begin
                f[v]:=g[t].lab;
                dfs(v);
                if lowlink[v]<lowlink[u] then
                    lowlink[u]:=lowlink[v];
                if lowlink[v]>dfn[u] then
                    ans[g[t].lab]:=1;
            end
            else
                if lowlink[u]>dfn[v] then
                    lowlink[u]:=dfn[v];
        end;
        t:=g[t].next;
    end;
end;

function getfa(t:longint):longint;
begin
    if fa[t]<>t then
        fa[t]:=getfa(fa[t]);
    getfa:=fa[t];
end;

begin
    readln(n,m);
    for i:=1 to m do
    begin
        readln(e[i].a,e[i].b,e[i].w);
        e[i].lab:=i;
    end;
    sort(1,m);
    for i:=1 to n do
        fa[i]:=i;
    now:=1;
    num:=0;
    repeat
        last:=now;
        while (last+1<=m) and (e[last].w=e[last+1].w) do
            inc(last);
        edgenum:=0;
        for i:=now to last do
        begin
            t1:=getfa(e[i].a);
            t2:=getfa(e[i].b);
            if t1=t2 then
                ans[e[i].lab]:=-1
            else
            begin
                first[t1]:=0;
                first[t2]:=0;
                dfn[t1]:=0;
                dfn[t2]:=0;
            end;
        end;
        sign:=0;
        edgenum:=0;
        for i:=now to last do
            if ans[e[i].lab]<>-1 then
            begin
                t1:=getfa(e[i].a);
                t2:=getfa(e[i].b);
                addedge(t1,t2,i);
                addedge(t2,t1,i);
            end;
        for i:=now to last do
        begin
            t1:=getfa(e[i].a);
            t2:=getfa(e[i].b);
            if t1<>t2 then
            begin
                fa[t2]:=t1;
                inc(num);
            end;
        end;
        for i:=now to last do
            if (ans[e[i].lab]<>-1) then
            begin
                t1:=getfa(e[i].a);
                if dfn[t1]=0 then
                    dfs(t1);
            end;
        now:=last+1;
        if num=n-1 then
        begin
            for i:=now to m do
                ans[e[i].lab]:=-1;
            now:=m+1;
        end;
    until now>m;
    for i:=1 to m do
        case ans[i] of
            -1:writeln('none');
            0:writeln('at least one');
            1:writeln('any');
        end;
end.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值