[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');}

 

posted @ 2020-07-22 16:36  月落乌啼算钱  阅读(159)  评论(0编辑  收藏  举报