最小树形图-朱刘算法学习笔记
首先我们对于一个有向图,先把除了根之外每个点边权最小的前驱加入答案,但是此时可能成环,于是我们对于一个环上的点
缩点过程用并查集实现即可。
Code:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;
const int MAXN = 1e4 + 10 , INF = 0x3f3f3f3f ;
typedef long long ll ;
int n , m , rt , mn[MAXN] , p[MAXN] , fa[MAXN] , tot , vis[MAXN] ;
ll ans ;
struct edge {
int u , v , w ;
edge (int u = 0 , int v = 0 , int w = 0) : u(u) , v(v) , w(w) {}
} e[MAXN << 1] ;
int main () {
scanf ("%d%d%d" , &n , &m , &rt) ;
for (int i = 1 ; i <= m ; i++)
scanf ("%d%d%d" , &e[i].u , &e[i].v , &e[i].w) ;
while (1) {
for (int i = 1 ; i <= n ; i++) mn[i] = INF , p[i] = fa[i] = vis[i] = 0 ;
for (int i = 1 ; i <= m ; i++) {
if (e[i].u == e[i].v) continue ;
if (e[i].w < mn[e[i].v]) mn[e[i].v] = e[i].w , fa[e[i].v] = e[i].u ;
}
mn[rt] = 0 ; tot = 0 ;
for (int i = 1 ; i <= n ; i++) {
if (mn[i] == INF) return !printf ("-1") ;
ans += mn[i] ;
}
for (int i = 1 ; i <= n ; i++) {
int x = i ;
for (; x != rt && vis[x] != i && !p[x] ; vis[x] = i , x = fa[x]) ;
if (x != rt && !p[x]) {
p[x] = ++tot ;
for (int j = fa[x] ; j != x ; j = fa[j]) p[j] = tot ;
}
}
if (!tot) break ;
for (int i = 1 ; i <= n ; i++) if (!p[i]) p[i] = ++tot ;
for (int i = 1 ; i <= m ; i++)
e[i].w -= mn[e[i].v] , e[i].u = p[e[i].u] , e[i].v = p[e[i].v] ;
rt = p[rt] ; n = tot ;
}
printf ("%lld\n" , ans) ;
return 0 ;
}