1. 程式人生 > 實用技巧 >APIO2013 道路費用【最小生成樹】

APIO2013 道路費用【最小生成樹】

題目連結

題目解析

根據\(k\)的範圍,不難想到我們\(2^k\)列舉所有新邊是否在\(MST\)中,然後再加入原始邊,計算出答案取最值。

但是這樣做複雜度過不去。

先考慮把\(k\)條新邊加進去,然後再按照\(Kruskal\)演算法加入\(n-1-k\)條原始邊,形成一棵樹。由於原始邊的權值各不相同,那麼目前加入的這些原始邊集合是唯一確定的。

又因為在\(MST\)裡的新邊條數最多為\(k\),那麼加入的原始邊數量不會比現在更少,又是從小到大加邊,目前已經加入的原始邊一定一直在\(MST\)中。

所以我們可以把這些固定的原始邊連在一起,進行縮點。

那麼現在圖中只剩下\(k+1\)個結點了(我們還差\(k\)

條邊沒有連,想成一棵\(k\)條邊的樹,結點個數就是\(k+1\)了)

誒嘿,然後我們現在發現剛才的那個做法,它又可以了,所以那麼做就行了。


►Code View

#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#include<cstring>
using namespace std;
#define N 100005
#define M 200005
#define INF 0x3f3f3f3f
#define LL long long
int rd()
{
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return f*x;
}
struct node{
	int u,v,w;
}e[M],g[25],e2[M];
int cnt;
bool cmp(node p,node q)
{
	return p.w<q.w;
}
int n,m,k;
int p[N],pe[N];
int id[N],tot;
int rt;
struct un{
	int f[N];
	void Init()
	{
		for(int i=1;i<=n;i++)
			f[i]=i,f1[i]=i;
	}
	int Find(int x)
	{
		if(f[x]==x) return x;
		return f[x]=Find(f[x]);
	}
	bool Union(int u,int v)
	{
		u=Find(u),v=Find(v);
		if(u==v) return 0;
		if(u<v) f[u]=v;
		else f[v]=u;
		return 1;
	}
}A,B;

int main()
{
	n=rd(),m=rd(),k=rd();
	A.Init();
	B.Init();
	for(int i=1;i<=m;i++)
		e[i].u=rd(),e[i].v=rd(),e[i].w=rd();
	for(int i=1;i<=k;i++)
	{
		g[i].u=rd(),g[i].v=rd();
		A.Union(g[i].u,g[i].v);
	}
	for(int i=1;i<=n;i++)
		p[i]=rd();
	sort(e+1,e+m+1,cmp);
	int num=k;
	for(int i=1;i<=m;i++)
	{
		if(A.Union(e[i].u,e[i].v))
		{
			num++;
			B.Union(e[i].u,e[i].v);
		}
		if(num==n-1) break;
	}
	for(int i=1;i<=n;i++)
		if(B.Find(i)==i)
			id[i]=++tot;//縮點
	for(int i=1;i<=n;i++)
		pe[id[B.Find(i)]]+=p[i];//縮點
	rt=id[B.Find(1)];
	A=B;
	for(int i=1;i<=m;i++)
		if(B.Union(e[i].u,e[i].v))
			e2[++cnt]=e[i];//可能加入的原始邊
	for(int i=1;i<=k;i++)
		g[i].u=id[A.Find(g[i].u)],g[i].v=id[A.Find(g[i].v)];//縮點
	for(int i=1;i<=cnt;i++)
		e2[i].u=id[A.Find(e2[i].u)],e2[i].v=id[A.Find(e2[i].v)];//縮點
	
	for(int S=0;S<(1<<k);S++)
	{
		
	}
	return 0;
}