洛谷 P2341 【受欢迎的牛】
- 题库:洛谷
- 题号:2341
- 题目:受欢迎的牛
- link:https://www.luogu.org/problemnew/show/P2341
思路:因为奶牛的爱慕关系具有传递性,所以每个环(强连通分量)里的奶牛是互相喜欢的。那么我们可以用到Tarjan算法把每个强连通分量找出,并缩点,把每个强连通分量都缩成一个点(当前缩点里的奶牛都是互相喜欢的)。这样一来,这个图就变成了一个DAG(有向无环图)。然后我们只需要统计每个缩点的出度就好了,如果一个点有出度&&我们知道这个图是一个DAG,所以这个强连通分量(这个缩点)里的奶牛就不可能被这个缩点所连出去的缩点里的奶牛所喜欢(这是无环图——DAG)。综上所述,我们只需要统计一下有多少个没有出度的强连通分量就好了,但有多个没有出度的强连通分量也不行,因为这样就会有两多群群奶牛不喜欢别的奶牛,使得奶牛们无法收到其他奶牛(这多群奶牛)的喜欢,这样就不行了 。
画一张图形象一下:
code :
1 #include <bits/stdc++.h> 2 #define INF 0x3f3f3f3f 3 using namespace std; 4 const int MAX = 5e4 + 1; 5 stack < int > pru; 6 int n, m, low[MAX], dfn[MAX], head[MAX], vis[MAX], col[MAX], sum[MAX], color, num, z, out[MAX], ans, cnt; 7 struct node 8 { 9 int next, to; 10 }stu[MAX]; 11 inline void add(int x, int y) 12 { 13 stu[++num].next = head[x];; 14 stu[num].to = y; 15 head[x] = num; 16 return; 17 } 18 inline void tarjan(int u)//Tarjan算法模板,这里用于缩点 19 { 20 low[u] = dfn[u] = ++z; 21 vis[u] = 1; 22 pru.push(u); 23 for(register int i = head[u]; i; i = stu[i].next) 24 { 25 int k = stu[i].to; 26 if(!vis[k]) 27 { 28 tarjan(k); 29 low[u] = min(low[u], low[k]); 30 } 31 else if(!col[k]) 32 { 33 low[u] = min(low[u], dfn[k]); 34 } 35 } 36 if(low[u] == dfn[u]) 37 { 38 col[u] = ++color; 39 ++sum[color];//当前强连通分量里的奶牛的个数++ 40 while(pru.top() != u) 41 { 42 col[pru.top()] = color; 43 ++sum[color];//当前强连通分量里的奶牛的个数++ 44 pru.pop(); 45 } 46 pru.pop(); 47 } 48 return; 49 } 50 signed main() 51 { 52 scanf("%d %d", &n, &m); 53 for(register int i = 1, x, y; i <= m; ++i) 54 { 55 scanf("%d %d", &x, &y); 56 add(y, x);//建反向边,把统计出度变为统计入度,更加方便一些 57 } 58 for(register int i = 1; i <= n; ++i) 59 { 60 if(!vis[i]) 61 { 62 tarjan(i); 63 } 64 } 65 for(register int u = 1; u <= n; ++u)//循环每个结点的出度(这里是入度,因为建的是反向边) 66 { 67 for(register int i = head[u]; i; i = stu[i].next) 68 { 69 int k = stu[i].to;//因为建的是反向边,所以i其实是k的出度 70 if(col[k] != col[u])//颜色不相同就代表不在一个强连通分量里 71 { 72 ++out[col[k]];//所以k的颜色(及包含k的那个强连通分量)就不能选了(这里标记为出度++),至于为什么思路里有讲 73 } 74 } 75 } 76 for(register int i = 1; i <= color; ++i)//枚举每种颜色(每个强连通分量) 77 { 78 if(!out[i])//要是没有出度(及当前强连通分量中没有奶牛喜欢别的奶牛) 79 { 80 ++cnt;//记录有几个强连通分量的缩点没有出度 81 ans = sum[i];//注意,这里是sum[i],因为i点只是当前强连通分量的缩点,真正被所有奶牛都喜欢的奶牛个数其实是这个强连通分量的大小(及当前强连通分量中奶牛的个数) 82 } 83 } 84 if(cnt == 1)//必须只有一个强连通分量没有出度,如果有多个也不行,因为这样那两个强连通分量里的奶牛都是不互相喜欢的 85 { 86 printf("%d", ans); 87 } 88 else 89 { 90 printf("0");//没有奶牛被所有奶牛喜欢 91 } 92 return 0; 93 }