链接:https://ac.nowcoder.com/acm/problem/15707
来源:牛客网
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
给出一个 0 ≤ N ≤ 105 点数、0 ≤ M ≤ 105 边数的有向图,
输出一个尽可能小的点集,使得从这些点出发能够到达任意一点,如果有多个这样的集合,输出这些集合升序排序后字典序最小的。
输入描述:
第一行为两个整数 1 ≤ n, m ≤ 105, 接下来 M 行,每行两个整数 1 ≤ u, v ≤ 105 表示从点 u 至点 v 有一条有向边。 数据保证没有重边、自环。
输出描述:
第一行输出一个整数 z,表示作为答案的点集的大小; 第二行输出 z 个整数,升序排序,表示作为答案的点集。
示例1
输入
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
#include<iostream>
#include<algorithm>
#include<vector>
#include<stack>
using namespace std;
const int N = 100000 + 10;
int n,m;
vector<int> v[N];
int dfn[N],low[N];
int tim;
stack<int> s;
int ans[N];
int cnt;
int vis[N];
int in[N];
vector<int> fina;
void tarjan(int x)
{
dfn[x] = low[x] = ++tim;
s.push(x);
for(int i=0; i<v[x].size(); i++)
{
int y = v[x][i];
if(!dfn[y])
{
tarjan(y);
low[x] = min(low[x], low[y]);
}
else if(!vis[y])//已确定环的不再遍历
low[x] = min(low[x], dfn[y]);
}
if(dfn[x] == low[x])
{
cnt++;
ans[cnt] = N;
int a;
do
{
a = s.top();
s.pop();
ans[cnt] = min(ans[cnt], a);
vis[a] = cnt;
}while(a != x);
}
}
int main()
{
cin>>n>>m;
for(int i=0; i<m; i++)
{
int x,y;
cin>>x>>y;
v[x].push_back(y);
}
for(int i=1; i<=n; i++)
{
if(!dfn[i]) tarjan(i);
}
for(int i=1; i<=n; i++)
{
for(int j=0; j<v[i].size(); j++)
{
if(vis[i] != vis[v[i][j]])//两个环可以联通,删掉其中一个的vis值
{
in[vis[v[i][j]]]++;
}
}
}
for(int i=1; i<=cnt; i++)
{
if(!in[i]) fina.push_back(ans[i]);
}
sort(fina.begin(), fina.end());
cout<<fina.size()<<endl;
for(int i=0; i<fina.size(); i++) cout<<fina[i]<<' ';
return 0;
}