Description
求最小树形图
n<=500,m<=n^2
Solution
老年选手开始写模板题
最小树形图定义:一个有向图,存在某个点为根,能到所有点并且边权最小的生成树
最小树形图的朱刘算法:
选择一个点r做根,O(VE)的时间求出以r为根的最小树形图
1’,对于每个非根节点,找到最小的入边in[x]
2’,将只保留in[x]的有向环(肯定是简单环)缩起来
3’,建立新图,保留所有非环边,(u,z,val)边权重设为val-in[v]
当没有环的时候便找到了最小树形图
对于没有给定根的情况,建立一个新点,向所有点连无穷大,以新点为根跑朱刘算法
可以发现最多有一条新边被选,若有>1条被选择择证明原图不连通,无最小树形图
Code
#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define rep(i,a) for(int i=lst[a];i;i=nxt[i])
using namespace std;
typedef long long ll;
int read() {
char ch;
for(ch=getchar();ch<'0'||ch>'9';ch=getchar());
int x=ch-'0';
for(ch=getchar();ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x;
}
const int N=505,M=N*N;
const ll inf=1e15;
int n,m,u[M],v[M],from[N],col[N],tmp;
ll z[M],in[M];
bool vis[N],in_stack[N];
void dfs(int x) {
if (!x) return;
if (from[x]==-1) return;
vis[x]=in_stack[x]=1;
if (!vis[from[x]]) dfs(from[x]);
else if (in_stack[from[x]]) {
col[x]=++tmp;
for(int i=from[x];i!=x;i=from[i]) col[i]=tmp;
}
in_stack[x]=0;
if (!col[x]) col[x]=++tmp;
}
int main() {
n=read();m=read();
fo(i,1,m) u[i]=read(),v[i]=read(),z[i]=read();
fo(i,1,n) {
++m;
u[m]=0;v[m]=i;z[m]=inf;
}
ll ans=0;
for(;;) {
fo(i,1,n) in[i]=inf<<1,from[i]=-1;
fo(i,1,m)
if (z[i]<in[v[i]]) {
in[v[i]]=z[i];
from[v[i]]=u[i];
}
fo(i,0,n) vis[i]=col[i]=0;tmp=0;
fo(i,1,n) {
ans+=in[i];
if (!vis[i]) dfs(i);
}
if (n==tmp) break;
int mm=0;
fo(i,1,m)
if (col[u[i]]!=col[v[i]]) {
z[++mm]=z[i]-in[v[i]];
u[mm]=col[u[i]];v[mm]=col[v[i]];
}
m=mm;n=tmp;
}
if (ans>=2*inf) puts("-1");
else printf("%lld\n",ans-inf);
return 0;
}