题目描述
给出一个 0 ≤ N ≤ 10
5 点数、0 ≤ M ≤ 10
5 边数的有向图,
输出一个尽可能小的点集,使得从这些点出发能够到达任意一点,如果有多个这样的集合,输出这些集合升序排序后字典序最小的。
输出一个尽可能小的点集,使得从这些点出发能够到达任意一点,如果有多个这样的集合,输出这些集合升序排序后字典序最小的。
输入描述:
第一行为两个整数 1 ≤ n, m ≤ 10
5,接下来 M 行,每行两个整数 1 ≤ u, v ≤ 10
5表示从点 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
题意
如上
题解
求有向图的最大强连通加上缩点,输出缩完后每个强连通子集的最小的点即可
强连通分量:有向图强连通分量:在有向图G中,如果两个顶点vi,vj间(vi>vj)有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通。如果有向图G的每两个顶点都强连通,称G是一个强连通图。有向图的极大强连通子图,称为强连通分量
缩点:因为强连通分量中的每两个点都是强连通的,可以将一个强连通分量当做一个超级点
代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=1e5+5; 4 vector<int>G[N],id[N]; 5 6 int low[N],dfn[N],instack[N],st[N],tot,cnt,scc,p; 7 int belong[N],in[N]; 8 void tarjan(int u) 9 { 10 int v; 11 low[u]=dfn[u]=++tot;//时间戳 12 st[++cnt]=u;//入栈 13 instack[u]=1; 14 for(int i=0;i<G[u].size();i++) 15 { 16 v=G[u][i]; 17 if(!dfn[v])//是否已经访问 18 { 19 tarjan(v); 20 low[u]=min(low[u],low[v]); 21 } 22 else if(instack[v])//已经访问过并且在栈里 23 { 24 low[u]=min(low[u],dfn[v]); 25 } 26 } 27 if(low[u]==dfn[u]) 28 { 29 scc++;//强连通 30 do{ 31 v=st[cnt--];//出栈 32 instack[v]=0;//v出栈后 33 belong[v]=scc;//v属于哪个强连通1-scc 34 id[scc].push_back(v);//当前强连通的子集 35 }while(u!=v); 36 } 37 } 38 int main() 39 { 40 int n,m,u[N],v[N]; 41 scanf("%d%d",&n,&m); 42 for(int i=1;i<=m;i++) 43 { 44 scanf("%d%d",&u[i],&v[i]); 45 G[u[i]].push_back(v[i]); 46 } 47 for(int i=1;i<=n;i++)//求所有强连通 48 if(!dfn[i])//是否已经访问过 49 tarjan(i); 50 for(int i=1;i<=m;i++)//缩点 51 { 52 if(belong[u[i]]==belong[v[i]])continue; 53 in[belong[v[i]]]++; 54 } 55 for(int i=1;i<=scc;i++) 56 sort(id[i].begin(),id[i].end()); 57 vector<int> res; 58 for(int i=1;i<=scc;i++) 59 if(!in[i]) 60 res.push_back(id[i][0]); 61 printf("%d\n%d",res.size(),res[0]); 62 for(int i=1;i<res.size();i++) 63 printf(" %d",res[i]); 64 return 0; 65 }