[LOJ6510]「雅礼集训 2018 Day8」A

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值