洛谷P2002 消息扩散

洛谷P2002 消息扩散

题目描述

\(n\) 个城市,中间有单向道路连接,消息会沿着道路扩散,现在给出 \(n\) 个城市及其之间的道路,问至少需要在几个城市发布消息才能让这所有 \(n\) 个城市都得到消息。

输入输出格式

输入格式:

第一行两个整数 \(n\), \(m\) 表示 \(n\) 个城市,\(m\) 条单向道路。

以下 \(m\) 行,每行两个整数 \(b\), \(e\) 表示有一条从 \(b\)\(e\) 的道路,道路可以重复或存在自环。

输出格式:

一行一个整数,表示至少要在几个城市中发布消息。

思路

tarjan缩点
缩点后必然会形成一片森林
然后我们可以发现对于森林中的每一棵树,只要在树的根节点发布消息,这棵树的所有节点就都可以收到消息
也就是说 \(至少发布消息数 = 缩点后的森林中树的个数\)
而森林中树的个数就是入度为 \(0\) 的节点个数

CODE

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stack>
using namespace std;
#define MAXN 100010
#define MAXM 500010
struct Node{
    int u,v;
    Node(){}
    Node(int u,int v):u(u),v(v){}
}p[MAXM],p2[MAXM];
int head[MAXN],head2[MAXN],Next[MAXM],Next2[MAXM],du[MAXN],low[MAXN],dfn[MAXN],color[MAXN];
bool instack[MAXN];
int i,j,k,m,n,u,v,tot,tot2,times,colnum,ans;
stack<int> mstack;
char readc;
void addNode(int u,int v){
    p[++tot]=Node(u,v);
    Next[tot]=head[u],head[u]=tot;
}
void addNode2(int u,int v){
    p2[++tot2]=Node(u,v);
    Next2[tot2]=head2[u],head2[u]=tot2;
}
void tarjan(int src){
    low[src]=dfn[src]=++times;
    mstack.push(src);
    instack[src]=true;
    for(register int i=head[src];i+1;i=Next[i]){
        if(!dfn[p[i].v]){
            tarjan(p[i].v);
            low[src]=min(low[src],low[p[i].v]);
        }else{
            if(instack[p[i].v]) low[src]=min(low[src],dfn[p[i].v]);
        }
    }
    if(dfn[src]==low[src]){
        colnum++;
        int tmp;
        do{
            tmp=mstack.top();
            mstack.pop(); instack[tmp]=false;
            color[tmp]=colnum;
        }while(tmp!=src);
    }
}
void read(int &n){
    while((readc=getchar())<48||readc>57);
    n=readc-48;
    while((readc=getchar())>=48&&readc<=57) n=n*10+readc-48;
}
int main(){
    read(n);
    read(m);
    memset(head,-1,sizeof(head));
    memset(head2,-1,sizeof(head2));
    tot=tot2=-1;
    for(i=1;i<=m;i++){
        read(u),read(v);
        addNode(u,v);
    }
    for(i=1;i<=n;i++){
        if(!low[i]){
            tarjan(i);
        }
    }
    for(i=1;i<=n;i++)
        for(j=head[i];j+1;j=Next[j]){
            if(color[p[j].u]!=color[p[j].v]){
                addNode2(color[p[j].u],color[p[j].v]);
                du[color[p[j].v]]++;
            }
        }
    ans=0;
    for(i=1;i<=colnum;i++)
        if(!du[i]) ans++;
    printf("%d\n",ans);
    return 0;
} 

转载于:https://www.cnblogs.com/linxif2008/p/10050792.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值