【NOIP2015模拟10.22】最小代价
前言
本来在比赛上就想到最小生成树了,但不相信这道题那么简单,然后就没有然后了。。。
题目
给出一幅由n个点m条边构成的无向带权图。
其中有些点是黑点,其他点是白点。
现在每个白点都要与他距离最近的黑点通过最短路连接(如果有很多个黑点,可以选取其中任意一个),我们想要使得花费的代价最小。请问这个最小代价是多少?
注意:最后选出的边保证每个白点到离它最近的黑点的距离仍然等于原图中的最短距离。
分析
这道题最麻烦的地方就是最终搞成的图有可能有很多个联通块。
增加一个点:0点,让0点连接所有的黑点,边权为0;
处理从S发到每个点的最短距离
题目要求最小的总距离,显然搞一遍最小生成树就可以了。
#include <cmath>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <queue>
const long long maxlongint=2147483747;
using namespace std;
long long b[710000][4],dis[710000],next[710000],last[710000],a[710000],fa[710000],ans,tot,n,m,v[710000],d[8000000];
bool bz[710000];
void q(long long l,long long r)
{
long long i=l,j=r;
long long mid=b[(l+r)/2][0],e;
while(i<j)
{
while(b[i][0]<mid) i++;
while(b[j][0]>mid) j--;
if(i<=j)
{
e=b[i][0];
b[i][0]=b[j][0];
b[j][0]=e;
e=b[i][1];
b[i][1]=b[j][1];
b[j][1]=e;
e=b[i][2];
b[i][2]=b[j][2];
b[j][2]=e;
i++;
j--;
}
}
if(i<r) q(i,r);
if(l<j) q(l,j);
}
int spfa()
{
long long head=0,tail=1,k;
fill(dis,dis+n+m+1,maxlongint*3);
fill(bz,bz+n+m+1,true);
d[1]=0;
dis[0]=0;
while(head<tail)
{
k=d[++head];
bz[k]=true;
for(long long i=last[k];i;i=next[i])
{
if(dis[a[i]]>dis[k]+v[i])
{
dis[a[i]]=dis[k]+v[i];
if(bz[a[i]])
{
d[++tail]=a[i];
bz[a[i]]=false;
}
}
}
}
}
bool dg(long long x)
{
long long i,j;
for(i=last[x];i;i=next[i])
{
long long y=a[i];
if(dis[x]+v[i]==dis[y])
{
dg(y);
b[++tot][1]=x;
b[tot][2]=y;
b[tot][0]=v[i];
}
}
return true;
}
long long get(long long x)
{
if(x==fa[x]) return x;
long long y=get(fa[x]);
fa[x]=y;
return y;
}
int kruskal()
{
long long i,j,k,l,x,y;
for(i=1;i<=tot;i++)
{
x=get(b[i][1]);
y=get(b[i][2]);
if(x!=y)
{
ans+=b[i][0];
fa[y]=x;
}
}
}
int bj(long long x,long long y,long long k)
{
fa[y]=y;
next[++tot]=last[x];
last[x]=tot;
v[tot]=k;
a[tot]=y;
}
int main()
{
scanf("%lld%lld",&n,&m);
long long i,j,k,l,x,y;
for(i=1;i<=n;i++)
{
scanf("%lld",&x);
if(x) bj(0,i,0);
}
for(i=1;i<=m;i++)
{
scanf("%lld%lld%lld",&x,&y,&k);
bj(x,y,k);
bj(y,x,k);
}
spfa();
tot=0;
dg(0);
q(1,tot);
bool b1=maxlongint;
kruskal();
if(ans==0) cout<<"impossible";else
cout<<ans;
}