题目大意
给出一副无向图,图中有黑点和白点,要求选择图中一些边建成新图,满足不会存在任何一个白点到最近的黑点距离超过原图中该白点到最近黑点的距离。
水题
建立虚拟结点连向所有黑点权值为0.
然后做一遍最短路,在最短路径中的边保留,然后做最小生成树。
#include<cstdio>
#include<algorithm>
#include<set>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const ll maxn=100000+10,maxm=200000+10,inf=10000000000000;
struct dong{
ll x,y,data;
};
dong edge[maxm*2];
ll h[maxn],f[maxn],go[maxm*3],dis[maxm*3],next[maxm*3],fa[maxn];
bool bz[maxn];
struct suan{
ll x,id;
friend bool operator <(suan a,suan b){
if (a.x<b.x) return 1;
else if (a.x==b.x&&a.id<b.id) return 1;
else return 0;
}
};
multiset<suan> s;
suan zlt;
ll i,j,k,l,t,n,m,tot,now,ans,cnt;
void add(ll x,ll y,ll z){
go[++tot]=y;
dis[tot]=z;
next[tot]=h[x];
h[x]=tot;
}
bool way(dong a,dong b){
return a.data<b.data;
}
ll getfa(ll x){
return fa[x]?fa[x]=getfa(fa[x]):x;
}
int main(){
scanf("%lld%lld",&n,&m);
fo(i,1,n){
scanf("%lld",&t);
if (t) add(1,i+1,0);
}
fo(i,1,m){
scanf("%lld%lld%lld",&j,&k,&l);
add(j+1,k+1,l);
add(k+1,j+1,l);
}
fo(i,2,n+1) f[i]=inf;
fo(i,1,n+1) zlt.x=f[i],zlt.id=i,s.insert(zlt);
fo(i,1,n){
now=(*s.begin()).id;
bz[now]=1;
zlt.x=f[now];
zlt.id=now;
s.erase(zlt);
t=h[now];
while (t){
if (f[now]+dis[t]<f[go[t]]){
zlt.id=go[t];
zlt.x=f[go[t]];
s.erase(zlt);
zlt.x=f[go[t]]=f[now]+dis[t];
s.insert(zlt);
}
t=next[t];
}
}
tot=0;
fo(i,1,n+1){
t=h[i];
while (t){
if (f[i]+dis[t]==f[go[t]]){
edge[++tot].x=i;
edge[tot].y=go[t];
edge[tot].data=dis[t];
}
t=next[t];
}
}
sort(edge+1,edge+tot+1,way);
fo(i,1,tot){
j=edge[i].x;k=edge[i].y;
j=getfa(j);
k=getfa(k);
if (j!=k){
fa[j]=k;
ans+=edge[i].data;
cnt++;
}
}
if (cnt==n) printf("%lld\n",ans);else printf("impossible\n");
}