2019.03.30【BZOJ4349】最小树形图(朱刘算法)

传送门


解析:

显然我们只需要考虑所有堡垒第一次攻打时候的花费,因为在所有堡垒攻打一遍之后,剩下的只需要一直用最小花费攻打就行了。

删去不用攻打的点,然后建立虚点,向所有点连边,权值为该点第一次攻打的花费。

剩下的边该连的就连上就行了。

然后以这个虚点为根求最小树形图就行了。

注意题目中有一个条件:保证攻打 u u u导致的 v v v的权值的改变一定比原来 v v v的权值小,如果去掉这个条件,就不能用最小树形图做了,读者可以想一想为什么。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define gc get_char
#define cs const

namespace IO{
	inline char get_char(){
		static cs int Rlen=1<<20|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	inline int getint(){
		re char c;
		while(!isdigit(c=gc()));re int num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
	
	inline double getdb(){
		re char c;
		while(!isdigit(c=gc()));re double x=c^48;
		while(isdigit(c=gc()))x=x*10+(c^48);
		if(c!='.')return x;
		re double y=1;
		while(isdigit(c=gc()))x+=(y/=10)*(c^48);
		return x; 
	}
}
using namespace IO;

using std::cout;
using std::cerr;

cs int N=52,M=1e4+4;
int n,m;

int u[M],v[M];
double w[M];

int id[N],pre[N],vis[N];
double in[N];
inline double DMST(int rt){
	double ans=0;
	while(true){
		for(int re i=1;i<=n;++i)in[i]=1e20;
		for(int re i=1;i<=m;++i)
		if(u[i]!=v[i]&&w[i]<in[v[i]])
		in[v[i]]=w[i],pre[v[i]]=u[i];
		
		int cnt=0;
		memset(vis+1,0,sizeof(int)*n);
		memset(id+1,0,sizeof(int)*n);
		for(int re i=1;i<=n;++i)if(i!=rt){
			ans+=in[i];
			int v=i;
			while(vis[v]!=i&&!id[v]&&v!=rt){
				vis[v]=i;
				v=pre[v];
			}
			if(!id[v]&&v!=rt){
				id[v]=++cnt;
				for(int re u=pre[v];u!=v;u=pre[u])id[u]=cnt;
			}
		}
		if(cnt==0)return ans;
		for(int re i=1;i<=n;++i)
		if(!id[i])id[i]=++cnt;
		for(int re i=1;i<=m;++i){
			int u=::u[i],v=::v[i];
			::u[i]=id[u],::v[i]=id[v];
			if(id[u]!=id[v])w[i]-=in[v];
		}
		rt=id[rt];
		n=cnt;
	}
}

double mn[N],ans;
int b[N];
int label[N];
signed main(){
	n=getint();
	for(int re i=1;i<=n;++i){
		double w=getdb();int nn=getint();
		if(nn){
			v[++m]=m;
			label[i]=m;
			mn[m]=::w[m]=w;
			b[m]=nn;
		}
	}
	n=m+1;
	for(int re i=1;i<n;++i)u[i]=n;
	int nowm=getint();
	while(nowm--){
		int x=label[getint()],y=label[getint()];double t=getdb();
		if(!x||!y||x==y)continue;
		u[++m]=x;v[m]=y;w[m]=t;
		mn[y]=std::min(mn[y],w[m]); 
	}
	for(int re i=1;i<n;++i)ans+=(b[i]-1)*mn[i];
	cout<<std::fixed<<std::setprecision(2)<<ans+DMST(n)<<"\n";
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值