题目背景
本场比赛第一题,给个简单的吧,这 100 分先拿着。
题目描述
有n个城市,中间有单向道路连接,消息会沿着道路扩散,现在给出n个城市及其之间的道路,问至少需要在几个城市发布消息才能让这所有n个城市都得到消息。
输入输出格式
输入格式:
第一行两个整数n,m表示n个城市,m条单向道路。
以下m行,每行两个整数b,e表示有一条从b到e的道路,道路可以重复或存在自环。
输出格式:
一行一个整数,表示至少要在几个城市中发布消息。
输入输出样例
输入样例#1:
5 4 1 2 2 1 2 3 5 1
输出样例#1:
2
说明
【数据范围】
对于20%的数据,n≤200;
对于40%的数据,n≤2,000;
对于100%的数据,n≤100,000,m≤500,000.
【限制】
时间限制:1s,内存限制:256M
【注释】
样例中在4,5号城市中发布消息。
tarjan算法将每个强连通分量缩成点,记录原先的每个点缩入了哪个点
缩完之后,扫描每条边,判断如果终点和起点所在的分量不同,标记终点
最后扫描为标记的的点计数器++也就是(入度为0的点)
#include <cstdio> #include <cstring> #include <iostream> using namespace std; int n,m,topp; #define N 100006 struct sta{ int sz[100001]; int top(){return sz[topp];} void push(int x){sz[++topp]=x;} void pop(){if(topp>0)topp--;} }stack; struct node{ int v,next; }edge[N*5];int head[N],num; int dfn[N],low[N],color[N];bool vis[N]; void add_edge(int x,int y) { edge[++num].v=y;edge[num].next=head[x];head[x]=num; } int cnt; void tarjan(int x) { dfn[x]=low[x]=++num; stack.push(x); vis[x]=1; for(int i=head[x];i;i=edge[i].next) { int v=edge[i].v; if(!dfn[v]) { tarjan(v); low[x]=min(low[x],low[v]); } else if(vis[v])low[x]=min(low[x],dfn[v]); } if(dfn[x]==low[x]) { ++cnt; int r; do{ r=stack.top(); color[r]=cnt; vis[r]=0; stack.pop(); }while(r!=x); } return ; } int main() { scanf("%d%d",&n,&m); int a,b; for(int i=1;i<=m;i++) { scanf("%d%d",&a,&b);add_edge(a,b); } num=0; for(int i=1;i<=n;i++) if(!dfn[i])tarjan(i); memset(vis,0,sizeof vis); for(int i=1;i<=n;i++) { for(int j=head[i];j;j=edge[j].next) { int v=edge[j].v; if(color[i]!=color[v]) { vis[color[v]]=1; } } } int ans=0; for(int i=1;i<=cnt;i++) if(!vis[i]) ans++; printf("%d ",ans); return 0; }