最小代价

题目大意

给出一副无向图,图中有黑点和白点,要求选择图中一些边建成新图,满足不会存在任何一个白点到最近的黑点距离超过原图中该白点到最近黑点的距离。

水题

建立虚拟结点连向所有黑点权值为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");
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值