POJ 2186 - Popular Cows - 强连通分量,缩点

Posted onlynagesha

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了POJ 2186 - Popular Cows - 强连通分量,缩点相关的知识,希望对你有一定的参考价值。

题目大意:

给定一个含N个点、M条边的有向图,求其中有多少个点,可以由其他任意一点出发到达它?

N<=1e4,M<=5e4。

为了描述和编程简便,我们建立原图的反图,这样问题转化为:有多少个点满足从它出发可以到达其他任意一点。

若无特殊说明,以下所指的图均为反图

引理1:满足条件的所有点必然在同一强连通分量内。

证明很简单,如果它们不在同一强连通分量内,那么其中必然有两点x,y使得x→y的路径不存在,与题目要求矛盾。

我们考虑求出该图的所有强连通分量,然后对于每个强连通分量,检验从其中任一点出发,能否到达其他所有的点。

我们可以采取缩点的方法,将每个强连通分量缩成一个点。

引理2:缩点后的图必然是一个有向无环图(DAG)。

证明:如果缩点后存在环,那么这个环可以合并成一个更大的强连通分量。这与强连通分量的极大性矛盾。

我们统计缩点后每个点的入度。如果有2个或以上的点入度为0,那么它们是互相不可达的,此时答案为0。

否则,如果只有一个点入度为0,答案就是该点所代表的强连通分量的大小。

实现时可以借助两个数组:sccId[x]表示点x所属的强连通分量的编号,sccSize[x]表示点x所属的强连通分量的大小。

参考:有向图强连通分量的Tarjan算法

代码:

  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 }

 

以上是关于POJ 2186 - Popular Cows - 强连通分量,缩点的主要内容,如果未能解决你的问题,请参考以下文章

Popular Cows(POJ 2186)

Poj2186Popular Cows

POJ 2186 Popular Cows

Popular Cows POJ - 2186(强连通分量)

POJ 2186 Popular Cows(Targin缩点)

POJ2186 Popular Cows [tarjan 缩点]