链接:https://www.nowcoder.com/acm/contest/81/C
来源:牛客网
题目描述
给出一个 0 ≤ N ≤ 105 点数、0 ≤ M ≤ 105 边数的有向图,
输出一个尽可能小的点集,使得从这些点出发能够到达任意一点,如果有多个这样的集合,输出这些集合升序排序后字典序最小的。
输入描述:
第一行为两个整数 1 ≤ n, m ≤ 105,
接下来 M 行,每行两个整数 1 ≤ u, v ≤ 105 表示从点 u 至点 v 有一条有向边。
数据保证没有重边、自环。
输出描述:
第一行输出一个整数 z,表示作为答案的点集的大小;
第二行输出 z 个整数,升序排序,表示作为答案的点集。
输入
7 10
4 5
5 1
2 5
6 5
7 2
4 2
1 2
5 3
3 5
3 6
输出
2
4 7
解题思路
Tarjan缩点之后,所有入度为0的强连通分量中最小的标号即为所求答案.
代码实现
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 1e6+7;
#define INF 0x3f3f3f3f
#define IO ios::sync_with_stdio(false);\
cin.tie(0);\
cout.tie(0);
struct node
{
int v,next;
} edge[maxn*2];
int dfn[maxn],low[maxn];
int sk[maxn];
int head[maxn],vis[maxn],cnt,tot,indexx;
int sccno[maxn],scc_num; //标记每个点所属的强连通分量的编号、标记强连通分量的编号
int mark_sccno_num[maxn];
int mark[maxn]; //标记强连通分量入度是否为0,若不是置为0
int ans[maxn];
void init()
{
memset(head,-1,sizeof(head));
memset(dfn,0,sizeof(dfn));
memset(sccno,-1,sizeof(sccno));
memset(mark_sccno_num,INF,sizeof(mark_sccno_num));
memset(vis,0,sizeof(vis));
tot=0,cnt=0,indexx=0,scc_num=0;
}
void add(int u,int v)
{
edge[tot].v=v;
edge[tot].next=head[u];
head[u]=tot++;
}
void tarjan(int u)
{
dfn[u]=low[u]=++cnt;
sk[++indexx]=u;
vis[u]=1;
for(int i=head[u]; i!=-1; i=edge[i].next)
{
int v=edge[i].v;
if(!dfn[v])
{
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(vis[v])
{
low[u]=min(low[u],dfn[v]);
}
}
if(low[u]==dfn[u])
{
++scc_num;
int t;
do
{
t=sk[indexx];
mark_sccno_num[scc_num]=min(mark_sccno_num[scc_num],t);
sccno[t]=scc_num;
vis[sk[indexx]]=0;
}
while(u!=sk[indexx--]);
}
}
void solve(int n)
{
for(int i=1; i<=n; i++)
if(!dfn[i]) tarjan(i);
for(int i=1; i<=scc_num; i++) mark[i]=i;
for(int u=1; u<=n; u++)
{
for(int i=head[u]; i!=-1; i=edge[i].next)
{
int v=edge[i].v;
if(sccno[u]!=sccno[v])
{
mark[sccno[v]]=0;
}
}
}
int an=0;
for(int i=1; i<=scc_num; i++)
{
if(mark[i])
ans[an++]=mark_sccno_num[i];
}
cout<<an<<endl;
sort(ans,ans+an);
cout<<ans[0];
for(int i=1; i<an; i++)
cout<<" "<<ans[i];
cout<<endl;
}
int main()
{
int n,m;
init();
cin>>n>>m;
int x,y;
for(int i=1; i<=m; i++)
{
cin>>x>>y;
add(x,y);
}
solve(n);
return 0;
}