【NOIP2016提高A组8.12】通讯

Description
“这一切都是命运石之门的选择。”
试图研制时间机器的机关SERN截获了中二科学家伦太郎发往过去的一条短信,并由此得知了伦太郎制作出了电话微波炉(仮)。
为了掌握时间机器的技术,SERN总部必须尽快将这个消息通过地下秘密通讯网络,传达到所有分部。
SERN共有N个部门(总部编号为0),通讯网络有M条单向通讯线路,每条线路有一个固定的通讯花费Ci。
为了保密,消息的传递只能按照固定的方式进行:从一个已知消息的部门向另一个与它有线路的部门传递(可能存在多条通信线路)。我们定义总费用为所有部门传递消息的费用和。
幸运的是,如果两个部门可以直接或间接地相互传递消息(即能按照上述方法将信息由X传递到Y,同时能由Y传递到X),我们就可以忽略它们之间的花费。
由于资金问题(预算都花在粒子对撞机上了),SERN总部的工程师希望知道,达到目标的最小花费是多少。
Input
多组数据,文件以2个0结尾。
每组数据第一行,一个整数N,表示有N个包括总部的部门(从0开始编号)。然后是一个整数M,表示有M条单向通讯线路。
接下来M行,每行三个整数,Xi,Yi,Ci,表示第i条线路从Xi连向Yi,花费为Ci。
Output
每组数据一行,一个整数表示达到目标的最小花费。
Sample Input
3 3
0 1 100
1 2 50
0 2 100
3 3
0 1 100
1 2 50
2 1 100
2 2
0 1 50
0 1 100
0 0
Sample Output
150
100
50
【样例解释】
第一组数据:总部把消息传给分部1,分部1再传给分部2.总费用:100+50=150.
第二组数据:总部把消息传给分部1,由于分部1和分部2可以互相传递消息,所以分部1可以无费用把消息传给2.总费用:100+0=100.
第三组数据:总部把消息传给分部1,最小费用为50.总费用:50.

Data Constraint
对于10%的数据,保证M=N-1
对于另30%的数据,N ≤ 20 ,M ≤ 20
对于100%的数据,N ≤ 50000 ,M ≤ 10^5 ,Ci ≤ 10^5 ,数据组数 ≤ 5
数据保证一定可以将信息传递到所有部门。

The Solution

题意:

给了一个含有 n (0<=N<=50000)  个节点的有向图,图中的两点之间的通信时要付出代价的(经过的边权之和),但是如果这两个点之间相互可达,代价为 0
问,从给定的节点向其他所有的点通信,所花费的最小代价是多少?

简单点说就是求把所有强连通分量连在一起所需的最小花费。

由于两个部门可以直接或间接地相互传递消息是,就可以忽
略花费。所以我们想到把强连通分量里的点缩在一起,然后就是
一个比较简单的问题了。对于一个点,我们贪心地来考虑,最优
方案必然是这个点所有入边中最小的那一条。

涉及:Tarjan缩点。

CODE

#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <stack>
#define fo(i,a,b) for (int i=a;i<=b;i++)
#define fd(i,a,b) for (int i=a;i>=b;i--)
#define N 50005
#define INF 2147483647
using namespace std;
stack<int> S;
int t[N*2],last[N*2],next[N*2],value[N*2],dfn[N],low[N],rd[N],Qy[N],l=0;
bool bz[N],v[N]; 
int tot,top,cnt;
inline int read()
{
    int x=0,w=1;
    char ch=getchar();
    while(ch>'9'||ch<'0'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*w;
}
void add(int x,int y,int z)
{
    t[++l]=y;
    next[l]=last[x];
    value[l]=z;
    last[x]=l;
}
void Tarjan(int x)
{
    S.push(x);
    dfn[x]=low[x]=++tot;  
    bz[x]=true;
    for (int k=last[x];k;k=next[k])
    {
        int v=t[k];
        if (dfn[v]==0) 
        {
            Tarjan(v);
            low[x]=min(low[v],low[x]);  
        }   
        else if (bz[v] && dfn[v]<low[x]) low[x]=dfn[v];
    }
    if (low[x]==dfn[x])
    {
        for (int v;1;)
        {
            v=S.top();
            rd[v]=cnt;
            bz[v]=false;
            S.pop();
            if (v==x) break;
        }
        cnt++;
    }
}
int main()
{
    int n,m;
    while (true)
    {
        n=read();m=read();
        if (n==0 && m==0) break;
        fill(last,last+n,0);
        l=0,cnt=0,tot=0,top=0;
        fo(i,1,m)
        {
            int x,y,z;
            x=read();y=read();z=read();
            add(x,y,z);
        }
        memset(bz,false,sizeof(bz));
        memset(dfn,0,sizeof(dfn));
        while(!S.empty()) S.pop();
        fo(i,0,n-1) if (dfn[i]==0) Tarjan(i);
        fo(i,0,cnt-1) Qy[i]=INF; 
        fo(i,0,n-1)
        {
            int u=rd[i];
            for (int k=last[i];k;k=next[k])
            {
                int v=rd[t[k]];
                if (u!=v) Qy[v]=min(Qy[v],value[k]);    
            }
        }
        int Ans=0;
        fo(i,0,cnt-1)
        {
            if (i==rd[0]) continue;
            Ans += Qy[i];
        }
        printf("%d\n",Ans);
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值