1. 程式人生 > >2018.12.08【NOIP提高組】模擬B組100042. 保留道路

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;
}