最小树形图

发布时间 2023-03-27 09:36:59作者: 风月无边~windymoon

最小树形图

  1. 求最短弧集合 \(E\)
    找到每个 \(u\) 点的最小入边 \(in[u]\) ,如果存在非根节点没有入边,则一定不存在树形图
for(ri int i=1;i<=m;++i){
    if(e[i].u^e[i].v&&e[i].w<in[e[i].v]){
        in[e[i].v]=e[i].w,pre[e[i].v]=e[i].u;
    }
}
for(ri int i=1;i<=n;++i) if(i^rt&&!pre[i]) return -1;
  1. 判断集合 \(E\) 中有没有有向环,如果有转步骤 \(3\) ,否则转 \(4\)
    一定要记录 \(cnt\) 或开一个 \(bool\) 数组记录走过的边,不然可能疯狂走环;
cnt=in[rt]=0; 
		for(ri int i=1,v;i<=n;++i){
    as+=in[v=i];
    while(vs[v]^i&&!id[v]&&v^rt) vs[v]=i,v=pre[v];
    if(!id[v]&&v^rt){
        id[v]=++cnt,v=pre[v];
        while(!id[v]) id[v]=cnt,v=pre[v];
    }            
}
if(!cnt) break;
  1. 收缩点,把有向环收缩成一个点,并且对图重新构建,包括边权值的改变和点的处理,之后再转步骤 \(1\)
    对于环外指向环的边 \((u \to v, w)\) ,其边权变为 \(w-in[v]\)
for(ri int i=1;i<=n;++i) if(!id[i]) id[i]=++cnt;
for(ri int i=1;i<=m;++i){
    if(id[e[i].u]^id[e[i].v]) e[i].w-=in[e[i].v];
    e[i].u=id[e[i].u],e[i].v=id[e[i].v];
} 
rt=id[rt],n=cnt;
  1. 展开收缩点,求得最小树形图
    如果不需要输出具体方案,直接返回 \(ans\) 即可
code
#include<bits/stdc++.h>
#define il inline
#define cs const
#define ri register
using namespace std;

namespace Q{
    il int rd(){
        ri int x=0;ri bool f=0;ri char c=getchar();
        while(c^EOF&&!isdigit(c)) f|=(c==45),c=getchar();
        while(c^EOF&&isdigit(c)) x=x*10+(c^48),c=getchar();
        return f?-x:x;
    }
    il void wt(int x){
        if(x<0) x=-x,putchar(45);
        if(x>=10) wt(x/10);
        return putchar(x%10|48),void();
    }
} using namespace Q;

cs int N=105,M=10004,inf=1<<30;
int in[N],pre[N],id[N],n,m,rt,vs[N];
struct qwq{int u,v,w;}e[M];

il int calc(){
    ri int as=0,cnt=0;
    while(1){
        for(ri int i=1;i<=n;++i) in[i]=inf,pre[i]=vs[i]=id[i]=0;
        for(ri int i=1;i<=m;++i){
            if(e[i].u^e[i].v&&e[i].w<in[e[i].v]){
                in[e[i].v]=e[i].w,pre[e[i].v]=e[i].u;
            }
        }
        for(ri int i=1;i<=n;++i) if(i^rt&&!pre[i]) return -1;
        cnt=in[rt]=0; 
		for(ri int i=1,v;i<=n;++i){
            as+=in[v=i];
            while(vs[v]^i&&!id[v]&&v^rt) vs[v]=i,v=pre[v];
            if(!id[v]&&v^rt){
                id[v]=++cnt,v=pre[v];
                while(!id[v]) id[v]=cnt,v=pre[v];
            }            
        }
        if(!cnt) break;
        for(ri int i=1;i<=n;++i) if(!id[i]) id[i]=++cnt;
        for(ri int i=1;i<=m;++i){
            if(id[e[i].u]^id[e[i].v]) e[i].w-=in[e[i].v];
            e[i].u=id[e[i].u],e[i].v=id[e[i].v];
        } 
        rt=id[rt],n=cnt;
    }
    return as;
}

signed main(){
    n=rd(),m=rd(),rt=rd();
    for(ri int i=1;i<=m;++i){
        e[i].u=rd(),e[i].v=rd(),e[i].w=rd();
    }
    wt(calc());
    return 0;
}