[20200721NOIP提高组模拟T3]最小代价
题目大意:
给你n个点$(n\leq100000)$所构成的边带权的无向图,每个点都有黑或白两种颜色之一,其中黑记为1,白记为0.现在要你选择一些边(选择一条边的代价即为此边权值),用这些边构成新的图,使得新图中每个白点都与黑点联通,且每个白点到距离其最近的黑点的距离等于原图中的最短距离.若不存在这样的图,输出impossible.
solution:
这题蛮神奇的,思路也不错,让我们来分析分析.首先,要求出每个白点到其最近的黑点的最短距离,我们可以通过新建一个超级点T,与每个黑点相连且边权为0.SPFA求出最短距离,然后想办法构图.我们来分析一下白点:对于一个白点$x$,加入到其最近的黑点与其直接相连,那直接将边权统计进去;加入这个白点与其它白点$y$相连,那白点y到黑点的最短路径一定是白点x到黑点的最短路径的子集,且仅差一条边,这条边将点$x$与$y$相连,所以只需将此边计入ans,因为其余路径点$y$可以统计.
code:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #include<queue> #define R register #define next exntttttttttttttttttttttt #define debug puts("mlg") using namespace std; typedef long long ll; typedef long double ld; typedef unsigned long long ull; inline ll read(); inline void write(ll x); inline void writesp(ll x); inline void writeln(ll x); ll n,m,T; ll col[200000]; ll From[500000],to[500000],head[500000],tot=1,c[500000],next[500000]; inline void add(ll x,ll y,ll z){ to[++tot]=y;From[tot]=x;next[tot]=head[x];head[x]=tot;c[tot]=z; } ll d[500000],pre[500000],last[500000]; queue<ll>q; bool vis[500000]; inline void spfa(){ memset(d,0x3f,sizeof d); d[T]=0;q.push(T); while(q.size()){ ll x=q.front();q.pop();vis[x]=false; for(R ll i=head[x],ver;i;i=next[i]){ ver=to[i]; if(d[ver]>d[x]+c[i]){ d[ver]=d[x]+c[i]; pre[ver]=x; last[ver]=i; if(!vis[ver]){ q.push(ver); vis[ver]=true; } } } } } ll ans; int main(){ n=read();m=read();T=n+1; for(R ll i=1;i<=n;i++){ col[i]=read(); if(col[i]){add(i,T,0);add(T,i,0);} } for(R ll i=1,x,y,z;i<=m;i++){ x=read();y=read();z=read(); if(x==y) continue; add(x,y,z);add(y,x,z); } spfa(); for(R ll i=1;i<=n;i++)if(!col[i]&&d[i]==d[0]){puts("impossible");return 0;} for(R ll x=1;x<=n;x++){ if(col[x]) continue; ans+=c[last[x]]; } writeln(ans); } inline ll read(){ll x=0,t=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') t=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*t;} inline void write(ll x){if(x<0){putchar('-');x=-x;}if(x<=9){putchar(x+'0');return;}write(x/10);putchar(x%10+'0');} inline void writesp(ll x){write(x);putchar(' ');} inline void writeln(ll x){write(x);putchar('\n');}