【Wannafly挑战赛14C可达性】【Tarjan缩点】
链接:https://www.nowcoder.com/acm/contest/81/C
来源:牛客网
题目描述
给出一个 0 ≤ N ≤ 105 点数、0 ≤ M ≤ 105 边数的有向图,
输出一个尽可能小的点集,使得从这些点出发能够到达任意一点,如果有多个这样的集合,输出这些集合升序排序后字典序最小的。
输入描述:
第一行为两个整数 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
题目大意:给一个有向图,求最少以哪几个点为起点能遍历全图
题目分析:由于强连通分量内的所有点都能走到彼此,所以可以先求出所有的强连通分量来进行缩点并且重构缩点之后的图。
【所谓缩点就是将整个连通块内化为一个点来进行操作,具体缩点操作就是求出连通块之后遍历所有边,把连通块看作是点,将连接不同连通块的边当作缩点之后的边】
由重构之后的图可知,入度不为0的"点"【也就是缩点之后的连通块】不需要取任何连通块内部的点就能被遍历到,所以只需在入度为0的"点"【连通块】找到字母序最小的内部点。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=100005; 4 struct edge{ 5 int from; 6 int to; 7 int next; 8 }EDGE[maxn]; 9 vector<int>vc[maxn]; 10 int head[maxn],dfn[maxn],vis[maxn],low[maxn],col[maxn],in[maxn],en[maxn],stk[maxn]; 11 int edge_cnt=1,tot1=1,tot2=0,scc_cnt=0,tot0=0; 12 void add(int x,int y) 13 { 14 EDGE[edge_cnt].from=x; 15 EDGE[edge_cnt].to=y; 16 EDGE[edge_cnt].next=head[x]; 17 head[x]=edge_cnt++; 18 } 19 void Tarjan(int u) 20 { 21 low[u]=dfn[u]=tot1++; 22 vis[u]=1; 23 stk[++tot2]=u; 24 for(int i = head[u]; i != -1 ; i = EDGE[i].next) 25 { 26 if(!dfn[EDGE[i].to]){ 27 Tarjan(EDGE[i].to); 28 low[u]=min(low[u],low[EDGE[i].to]); 29 } 30 else if(vis[EDGE[i].to]){ 31 low[u]=min(low[u],low[EDGE[i].to]); 32 } 33 } 34 if(low[u]==dfn[u]){ 35 int xx; 36 scc_cnt++; 37 do{ 38 xx=stk[tot2--]; 39 vc[scc_cnt].push_back(xx); 40 col[xx]=scc_cnt; 41 vis[xx]=0; 42 }while(xx!=u); 43 } 44 } 45 int main() 46 { 47 int n,m; 48 scanf("%d%d",&n,&m); 49 memset(head,-1,sizeof(head)); 50 memset(in,0,sizeof(in)); 51 while(m--) 52 { 53 int a,b; 54 scanf("%d%d",&a,&b); 55 add(a,b); 56 } 57 for(int i = 1 ; i <= n; i++) 58 { 59 if(!dfn[i])Tarjan(i); 60 } 61 for(int i = 1 ; i <= edge_cnt ; i++) 62 { 63 if(col[EDGE[i].from]!=col[EDGE[i].to]) 64 { 65 in[col[EDGE[i].to]]++; 66 } 67 } 68 for(int i = 1 ; i <= scc_cnt ; i++) 69 { 70 if(in[i]) 71 continue; 72 int mmin=vc[i][0]; 73 for(int j = 1 ; j < vc[i].size() ; j++) 74 { 75 if(vc[i][j]<mmin) 76 mmin=vc[i][j]; 77 } 78 en[tot0++]=mmin; 79 } 80 81 printf("%d\n",tot0); 82 sort(en,en+tot0); 83 for(int i = 0 ; i < tot0 ; i++) 84 { 85 printf("%d",en[i]); 86 char c=(i==tot0-1)?'\n':' '; 87 printf("%c",c); 88 } 89 return 0; 90 } 91 /*4 5 92 1 3 93 2 4 94 4 2 95 1 4 96 2 1*/