通讯 【NOIP2016提高A组8.12】

题目

“这一切都是命运石之门的选择。”
试图研制时间机器的机关SERN截获了中二科学家伦太郎发往过去的一条短信,并由此得知了伦太郎制作出了电话微波炉(仮)。
为了掌握时间机器的技术,SERN总部必须尽快将这个消息通过地下秘密通讯网络,传达到所有分部。
SERN共有N个部门(总部编号为0),通讯网络有M条单向通讯线路,每条线路有一个固定的通讯花费Ci。
为了保密,消息的传递只能按照固定的方式进行:从一个已知消息的部门向另一个与它有线路的部门传递(可能存在多条通信线路)。我们定义总费用为所有部门传递消息的费用和。
幸运的是,如果两个部门可以直接或间接地相互传递消息(即能按照上述方法将信息由X传递到Y,同时能由Y传递到X),我们就可以忽略它们之间的花费。
由于资金问题(预算都花在粒子对撞机上了),SERN总部的工程师希望知道,达到目标的最小花费是多少。

样例输入:
多组数据,文件以2个0结尾。
每组数据第一行,一个整数N,表示有N个包括总部的部门(从0开始编号)。然后是一个整数M,表示有M条单向通讯线路。
接下来M行,每行三个整数,Xi,Yi,Ci,表示第i条线路从Xi连向Yi,花费为Ci。
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

样例输出:
150
100
50

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


剖解题目

给一张有向带权图,0为起点,共N个点。图中强连通分量间的点互相传递不需要花费,否则需要花费该带权边上的数值,问从0开始传递,最少传递完整张图的代价。


思路

看到强连通分量,很自然就tarjan算法,然后对于新图搞一下就好了。


解法

10%:友情送分
30%:枚举保留的边,取min,时间复杂度 O(2m)
100%:先tarjan一遍求出所有的强连通分量,然后把每一块强连通分量缩成一个点,对于每一条和强连通分量内的点有关的边,都全部连在这个缩点上,这样就构成了一张新的图。然后就是贪心的对于每一个非0点取一条到达它的最小的边就行了。

PS:这道题细节真他喵多!近乎改了我一个下午加晚上,主要是数据稍大时候要手画时比较麻烦,画的我自己都晕乎乎的。


代码

#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)

using namespace std;

const int maxn=50005,maxm=100005;
int fre[maxn],next[maxm],go[maxm],n,m,num,dfn[maxn],low[maxn],val[maxm],k,stack[maxn],top,p;
int t[maxn],ans,b[maxn];
bool bz[maxn],sbz[maxn];
struct cy{
    int x,y,z;
}a[maxm];

void add(int x,int y,int z)
{
    go[++num]=y;
    next[num]=fre[x];
    fre[x]=num;
    val[num]=z;
}
void tarjan(int x)
{
    dfn[x]=++k;
    low[x]=k;
    bz[x]=true;
    sbz[x]=true;
    stack[++top]=x;
    int i=fre[x];
    while (i){
        if (!dfn[go[i]]){
            tarjan(go[i]);
            low[x]=min(low[x],low[go[i]]);
        }
        else if (sbz[go[i]]) low[x]=min(low[x],dfn[go[i]]);
        i=next[i];
    }
    if (dfn[x]==low[x]){
        ++p;
        int j=0;
        while (x!=stack[top]){
            ++j;
            t[stack[top]]=p;
            sbz[stack[top]]=false;
            stack[top]=0;
            --top;
        }
        sbz[stack[top]]=false;
        stack[top]=0;
        --top;
        ++j;
        if (j==1) --p;
        else t[x]=p;
    }
} 
int main()
{
//  freopen("T2.in","r",stdin);
    scanf("%d%d",&n,&m);
    while (n!=0 && m!=0) {      
        fo(i,1,m){
            scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
            ++a[i].x;
            ++a[i].y;
            if (a[i].x==a[i].y) {
                a[i].z=maxm;
                continue;
            }
            add(a[i].x,a[i].y,a[i].z);
        }

        p=n;
        k=0;
        fo(i,1,n)
        if (!bz[i]) tarjan(i);
        fo(i,1,max(n,m)){
            fre[i]=0; next[i]=0; go[i]=0; val[i]=0;
        }

        num=0;
        fo(i,1,m){
            if (a[i].x==a[i].y) continue;
            if (!t[a[i].x] && t[a[i].y]) add(a[i].x,t[a[i].y],a[i].z);
            else if (t[a[i].x] && !t[a[i].y]) add(t[a[i].x],a[i].y,a[i].z);
            else if (!t[a[i].x] && t[a[i].x]==t[a[i].y]) add(a[i].x,a[i].y,a[i].z);
            else if (t[a[i].x] && t[a[i].x]!=t[a[i].y] && t[a[i].y]) 
                add(t[a[i].x],t[a[i].y],a[i].z);
        }

        if (!num) printf("0\n");
        else {
            ans=0;
            bool bo[maxn];
            fo(i,1,p) {
                b[i]=maxm;
                bo[i]=false;
            }
            fo(i,1,n)
            {
                int u=i;
                if (t[i]) {
                    b[i]=0;
                    u=t[i];
                    if (i==1) b[u]=0;
                }
                if (bo[u]) continue;
                bo[u]=true;
                int x=fre[u];
                while (x){
                    if (val[x]<b[go[x]]) b[go[x]]=val[x];
                    x=next[x];
                }
            }
            memset(bo,0,sizeof(bo));
            fo(i,2,n)
            if (!bo[i]){
                int u=i;
                if (t[i]) u=t[i];
                if (bo[u]) continue;
                bo[u]=true;
                ans+=b[u];
            }
            printf("%d\n",ans);
        } 

        num=0; k=0; top=0;
        fo(i,0,p){
            bz[i]=false; sbz[i]=false;
            dfn[i]=0; low[i]=0;
            t[i]=0; fre[i]=0;
            next[i]=0;
        }
        memset(a,0,sizeof(a));

        scanf("%d%d",&n,&m);
    } 
    //fclose(stdin);
}

这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值