4349: 最小树形图

建一个rt,对所有点连一条边,边权为原代价。
再读入其他边,跑最小树形图。
最后答案再加上每个点的最小入边*(攻打次数-1)
玄学内存题。不要看我的define
#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
#include<algorithm>
#include<map>
#include<cmath>
#include<queue>
using namespace std;
#define rep(i,j,k) for(i=j;i<=k;++i)
#define per(i,j,k) for(i=j;i>=k;--i)
#define sqr(x) ((x)*(x))
#define G getchar()
#define LL long long
#define pdi pair<double,int>
#define mkp make_pair
#define X first
#define Y second
#define N 2005
#define NN 100000
#define inf 5000001
struct EDGE{int a,b;double w;bool flg;}e[NN];
int n,m,rt,tot,cs[N],pre[N],blg[N],cnt,vis[N];
double ans,Min[N];
int main(){
//	freopen("r.in","r",stdin);
//	freopen("w.out","w",stdout);
	int i,x,y;double z;bool jg;
	scanf("%d",&n);rt=cnt=n+1;
	rep(i,1,n){
		scanf("%lf%d",&z,&cs[i]);
		e[++tot]=(EDGE){rt,i,Min[i]=z,0};
	}
	for(scanf("%d",&m);m--;){
		scanf("%d%d%lf",&x,&y,&z);
		Min[y]=min(Min[y],z);
		if(x!=y)e[++tot]=(EDGE){x,y,z};
	}
	rep(i,1,n<<1)ans+=Min[i]*(cs[i]-1),blg[i]=i;
	while(1){
		rep(i,1,cnt)Min[i]=inf,pre[i]=vis[i]=0;jg=false;
		rep(i,1,tot){
			vis[i]=false;
			if(!e[i].flg&&Min[e[i].b]>e[i].w)
				Min[e[i].b]=e[i].w,pre[e[i].b]=i;
		}
		rep(i,1,cnt)if(blg[i]==i&&!vis[i]){
			vis[i]=i;
			for(x=e[pre[i]].a;x&&!vis[x];x=e[pre[x]].a)vis[x]=i;
			if(vis[x]==i){
				y=x;
				e[pre[y]].flg=jg=true;ans+=e[pre[y]].w;blg[y]=++cnt;
				for(x=e[pre[y]].a;x!=y;x=e[pre[x]].a)
					ans+=e[pre[x]].w,blg[x]=cnt,e[pre[x]].flg=1;
			}
		}
		if(!jg)break;
		rep(i,1,tot)if(!e[i].flg){
			if(blg[e[i].b]!=e[i].b){
				e[i].w-=Min[e[i].b];e[i].b=blg[e[i].b];
			}
			e[i].a=blg[e[i].a];
			if(e[i].a==e[i].b)e[i].flg=1;
		}
	}
	rep(i,1,cnt)if(i!=n+1&&blg[i]==i)ans+=Min[i];
	printf("%.2lf\n",ans);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值