题目描述
给出一幅由n个点m条边构成的无向带权图。
其中有些点是黑点,其他点是白点。
现在每个白点都要与他距离最近的黑点通过最短路连接(如果有很多个黑点,可以选取其中任意一个),我们想要使得花费的代价最小。请问这个最小代价是多少?
注意:最后选出的边保证每个白点到离它最近的黑点的距离仍然等于原图中的最短距离。
Input
第一行两个整数n,m;
第二行n 个整数,0表示白点,1 表示黑点;
接下来m 行,每行三个整数x,y,z,表示一条连接x和y 点,权值为z 的边。
Output
如果无解,输出impossible;
否则,输出最小代价。
Sample Input
5 7
0 1 0 1 0
1 2 11
1 3 1
1 5 17
2 3 1
3 5 18
4 5 3
2 4 5
Sample Output
5
【样例解释】
选 2、4、6三条边
Data Constraint
对30%的输入数据: 1≤n≤10, 1≤m≤20;
对100%的输入数据:1≤n≤100000,1≤m≤200000,1≤z≤1000000000
分析
我们容易求出所有点到最近黑点的值:只要反过来,对黑点求到其他点的最短路即可,但是= =
多点最短路你做过吗?
容易想到建超级点(有点像网络流是吧)连接所有黑点,边权0,单向边
那么对超级点跑最短路即可
【其中超级点延伸的最短路必定只经过一个黑点,因为经过大于一个肯定不优】
求出值以后,我们看一下:
如果有边
那么我们发现这3条边都是1到3最短路中的一部分
但是不难想到边越多越优(这个可以自己想想),然后最短路上边越多即边权越小,所以跑一个最小生成树用并查集优化,树权即为答案(impossible只有黑点都与白点不连通的情况,所以树权为0就不存在解了)
#include <cstdio>
#include <algorithm>
#include <queue>
#include <memory.h>
#define rep(i,a,b) for (i=a;i<=b;i++)
typedef long long ll;
const int N=100011;
using namespace std;
int n,m;
int color[N];
struct Edge {
int u,v,nx;
ll w;
}g[4*N],e[2*N];
int cnt,ecnt,list[N];
ll d[N],ans;
bool b[4*N],p,l[N];
int f[N];
void Add(int u,int v,ll w) {g[++cnt].u=u;g[cnt].v=v;g[cnt].w=w;g[cnt].nx=list[u];list[u]=cnt;}
void Adde(int u,int v,ll w) {e[++ecnt].u=u;e[ecnt].v=v;e[ecnt].w=w;e[ecnt].nx=list[u];list[u]=ecnt;}
int Get_f(int x) {return f[x]==x?x:f[x]=Get_f(f[x]);}
void Merge(int i,int j) {f[i]=j;}
bool Cmp(Edge a,Edge b) {return a.w>b.w;}
void Spfa(int v0) {
queue<int> q;
while (!q.empty()) q.pop();
q.push(v0);
d[v0]=0;
while (!q.empty()) {
int u=q.front();q.pop();
for (int i=list[u];i;i=g[i].nx)
if (d[g[i].v]>d[u]+g[i].w) {
d[g[i].v]=d[u]+g[i].w;
q.push(g[i].v);
}
}
}
void Dfs(int u,ll w) {
for (int i=list[u];i;i=g[i].nx)
if (w+g[i].w==d[g[i].v]) {
Dfs(g[i].v,w+g[i].w);
b[i]=1;
}
}
ll Kruskal() {
int i,x,y;
sort(e+1,e+ecnt+1,Cmp);
rep(i,1,ecnt)
if ((x=Get_f(e[i].u))!=(y=Get_f(e[i].v))) {
Merge(x,y);
ans+=e[i].w;
}
return ans;
}
int main() {
int i;
scanf("%d%d",&n,&m);
rep(i,1,n)
scanf("%d",&color[i]);
rep(i,1,m) {
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
Add(u,v,w);Add(v,u,w);
}
memset(d,0x3f,sizeof d);
rep(i,1,n)
if (color[i]) Add(n+1,i,0);
Spfa(n+1);
Dfs(n+1,0);
rep(i,1,n) f[i]=i;
memset(list,0,sizeof(list));
rep(i,1,cnt)
if (b[i])
Adde(g[i].u,g[i].v,g[i].w);
Kruskal();
if (!ans) printf("impossible");
else printf("%lld",ans);
}