链接:https://www.nowcoder.com/acm/contest/81/C
来源:牛客网
时间限制: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
分析:首先,所有的入度为0的点必然要放入到集合中,否则不会最优。排除掉这些点后,剩下的点组成的图中,没有入度为0的点,则必然有环,将环进行缩点,以环中最小的点的编号作为其编号,找到新的入度为0的点,放入集合中。
代码如下:
#include <cstdio> #include <cstring> #include <string> #include <algorithm> #include <iostream> #include <vector> #include <stack> using namespace std; const int MAXN=1e5+100; int dfn[MAXN],low[MAXN],vis[MAXN]; int in[MAXN]; int root[MAXN]; vector<int>path[MAXN]; vector<int>ans; stack<int>S; int n,m,u,v,tot; struct node { int u; int v; }edge[MAXN]; void tarjan(int x) { low[x]=dfn[x]=++tot; S.push(x);vis[x]=1; for(int i=0;i<path[x].size();i++) { int v=path[x][i]; if(!dfn[v]) { tarjan(v); low[x]=min(low[x],low[v]); } else if(vis[v]) low[x]=min(low[x],low[v]); } if(low[x]==dfn[x]) { while(!S.empty()) { int now=S.top(); root[now]=x; vis[now]=0; S.pop(); if(now==x)break; } } } int main() { tot=0; scanf("%d%d",&n,&m); for(int k=1;k<=m;k++) { scanf("%d%d",&u,&v); edge[k].u=u,edge[k].v=v; path[u].push_back(v); } for(int i=1;i<=n;i++) { if(!dfn[i]) tarjan(i); } for(int i=1;i<=m;i++) { if(root[edge[i].u]!=root[edge[i].v]) in[root[edge[i].v]]++; } for(int i=1;i<=n;i++) { int r=root[i]; if(in[r]==0) ans.push_back(r); } sort(ans.begin(),ans.end()); ans.erase(unique(ans.begin(),ans.end()),ans.end()); cout<<ans.size()<<endl; for(int i=0;i<ans.size();i++) i==0?cout<<ans[i]:cout<<" "<<ans[i]; cout<<endl; return 0; }