题目大意:
给定一个含N个点、M条边的有向图,求其中有多少个点,可以由其他任意一点出发到达它?
N<=1e4,M<=5e4。
为了描述和编程简便,我们建立原图的反图,这样问题转化为:有多少个点满足从它出发可以到达其他任意一点。
若无特殊说明,以下所指的图均为反图。
引理1:满足条件的所有点必然在同一强连通分量内。
证明很简单,如果它们不在同一强连通分量内,那么其中必然有两点x,y使得x→y的路径不存在,与题目要求矛盾。
我们考虑求出该图的所有强连通分量,然后对于每个强连通分量,检验从其中任一点出发,能否到达其他所有的点。
我们可以采取缩点的方法,将每个强连通分量缩成一个点。
引理2:缩点后的图必然是一个有向无环图(DAG)。
证明:如果缩点后存在环,那么这个环可以合并成一个更大的强连通分量。这与强连通分量的极大性矛盾。
我们统计缩点后每个点的入度。如果有2个或以上的点入度为0,那么它们是互相不可达的,此时答案为0。
否则,如果只有一个点入度为0,答案就是该点所代表的强连通分量的大小。
实现时可以借助两个数组:sccId[x]表示点x所属的强连通分量的编号,sccSize[x]表示点x所属的强连通分量的大小。
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <stack> 5 6 #define FILLARR(arr, ch) memset(arr, ch, sizeof(arr)) 7 8 const int maxN = (int)1e4 + 5; 9 const int maxM = (int)5e4 + 5; 10 11 struct Edge 12 { 13 int to, next; 14 void assign(int t, int n) 15 { 16 to = t; 17 next = n; 18 } 19 }; 20 21 Edge elist[maxM]; 22 int head[maxN]; 23 int ecnt; 24 int N, M; 25 26 void initElist() 27 { 28 FILLARR(head, -1); 29 ecnt = 0; 30 } 31 32 inline void addEdge(int from, int to) 33 { 34 elist[ecnt].assign(to, head[from]); 35 head[from] = (ecnt++); 36 } 37 38 void input() 39 { 40 scanf("%d%d", &N, &M); 41 initElist(); 42 for (int u, v, i = 0; i < M; i++) 43 { 44 scanf("%d%d", &u, &v); 45 addEdge(v, u); 46 } 47 } 48 49 int dfn[maxN]; 50 int low[maxN]; 51 bool inStk[maxN]; 52 int sccId[maxN]; 53 int sccSize[maxN]; 54 int lastDfn = 0; 55 std::stack<int> stk; 56 57 inline void pushToStk(int v) 58 { 59 stk.push(v); 60 inStk[v] = true; 61 } 62 63 inline int popFromStk() 64 { 65 int v = stk.top(); 66 stk.pop(); 67 inStk[v] = false; 68 return v; 69 } 70 71 void dfs(int cur) 72 { 73 dfn[cur] = low[cur] = (++lastDfn); 74 pushToStk(cur); 75 76 for (int e = head[cur]; e != -1; e = elist[e].next) 77 { 78 int to = elist[e].to; 79 if (dfn[to] == 0) 80 { 81 dfs(to); 82 low[cur] = std::min(low[cur], low[to]); 83 } 84 else if (inStk[to]) 85 low[cur] = std::min(low[cur], dfn[to]); 86 } 87 88 if (dfn[cur] == low[cur]) 89 { 90 for (int v = popFromStk(); ; v = popFromStk()) 91 { 92 sccId[v] = cur; 93 sccSize[cur] += 1; 94 if (v == cur) 95 break; 96 } 97 } 98 } 99 100 int inDeg[maxN]; //compressed graph in which each SCC is compressed into one node 101 102 int solve() 103 { 104 for (int i = 1; i <= N; i++) 105 if (dfn[i] == 0) 106 dfs(i); 107 108 for (int i = 1; i <= N; i++) 109 inDeg[i] = (sccId[i] == i ? 0 : -1); 110 111 for (int i = 1; i <= N; i++) 112 for (int e = head[i]; e != -1; e = elist[e].next) 113 { 114 int to = elist[e].to; 115 if (sccId[i] != sccId[to]) 116 inDeg[sccId[to]] += 1; //link between two SCCs 117 } 118 119 int head = (int)(std::find(inDeg + 1, inDeg + N + 1, 0) - inDeg); 120 return std::count(inDeg + head + 1, inDeg + N + 1, 0) > 0 ? 0 : sccSize[head]; 121 } 122 123 int main() 124 { 125 input(); 126 printf("%d", solve()); 127 return 0; 128 }