2018.12.08【NOIP提高组】模拟B组100042. 保留道路

原来的思路是排序g,枚举最大的g,接着将g边权小于这个g的边排序做mst。
O(m^2)
我们发现,后面的步骤可以简化,排完g后,将边逐条加入一个边集b中。
i :1-> m 枚举最大的g,(同上)(这条一定要选)mst的答案。
现在我们有n条边,其中n-1条是我们之前mst筛下的,还有一条(i)是新加的
假设边集b的s本来是有序的,我们只需将i找一个位置插入(比较s值)即可
再用这n条边mst
证明:
假如现在固定了g值最大值,设为G,一共有l条边(边权都<=G),现在按s属性做mst,保留了n-1条边,显然剩下的l-(n-1)条边就没用了,因为剩下的边不会对后面的答案有影响(对于s这个属性,它们没有那n-1条边优,就算看g,i不断往后,后面的边g边权不断增大,与它们的取值无关)

#include<bits/stdc++.h>
#define ll long long
#define N 410
#define M 50010
#define inf (1ll<<60)
#define open(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout);
using namespace std;

struct edge
{	ll u,v,g,s;
}e[M];
ll n,m,i,j,t,Wg,Ws,tot,cur,ans=inf,father[N],b[M];

bool cmpg(edge x,edge y)
{return x.g<y.g;}

ll getfather(ll x)
{return father[x]==x?x:father[x]=getfather(father[x]);}

int main()
{
	//open("road");
	scanf("%lld%lld%lld%lld",&n,&m,&Wg,&Ws);
	for(i=1;i<=m;i++) scanf("%lld%lld%lld%lld",&e[i].u,&e[i].v,&e[i].g,&e[i].s);
	sort(e+1,e+1+m,cmpg);
	for(i=1;i<=m;i++)
	{
		cur=tot+1;
		for(j=1;j<=tot;j++)
		if(e[b[j]].s > e[i].s )
		{cur=j;break;}
		++tot;
		if(cur==tot)
			b[cur]=i;
		else
		{
			for(j=tot;j>cur;j--)b[j]=b[j-1];
			b[cur]=i;
		}
		ll cnt=0,flag=0;
		for(j=1;j<=n;j++)father[j]=j;
		for(j=1;j<=tot;j++)
		{
			ll fx=getfather(e[b[j]].u),fy=getfather(e[b[j]].v);
			if(fx!=fy)
			{
				father[fx]=fy;
				b[++cnt]=b[j];
				if(b[j]==i)flag=1;
			}
		}
		if(flag && cnt== n-1)ans=min(ans,Wg*e[i].g+Ws*e[b[cnt]].s);
		tot=cnt;
	}
	printf("%lld",ans==inf?-1:ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Com_man_der/article/details/84900187