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;
}
转载请注明出处,有疑问欢迎探讨
博主邮箱 2775182058@qq.com