洛谷P2341 受欢迎的牛(tarjan强连通分量模板)
题目链接:洛谷P2341 受欢迎的牛
题意
每头奶牛都梦想成为牛棚里的明星。被所有奶牛喜欢的奶牛就是一头明星奶牛。所有奶牛都是自恋狂,每头奶牛总是喜欢自己的。奶牛之间的“喜欢”是可以传递的——如果A喜欢B,B喜欢C,那么A也喜欢C。牛栏里共有N 头奶牛,给定一些奶牛之间的爱慕关系,请你算出有多少头奶牛可以当明星。
思路
由题可得,受欢迎的奶牛只有可能是图中唯一的出度为零的强连通子图中的所有奶牛,所以若出现两个以上出度为0的强连通子图则不存在明星奶牛,因为那若干个出度为零的子图的爱慕无法传递出去。那唯一的子图能受到其他子图的爱慕同时可以在子图内相互传递,所以该子图中的所有奶牛都是明星。
时间复杂度$O(n+m)$代码实现
#include <iostream> #include <cstdio> #include <cstring> #include <stack> using std::min; using std::stack; const int N = 10010, M = 50010; struct Edge { int to, nex; } edge[M]; int num_e; int head[N], du[N]; // scc: scc[u]表示u所在的强联通子图编号; sz[i]表示第i个强联通子图规模; idx: dfs时间戳; tot: 统计强联通子图个数; int dfn[N], low[N], scc[N], sz[N], idx, tot; bool insta[N]; int sta[N], top; void init() { num_e = 0, top = 0, idx = 0, tot = 0; memset(head, 0, sizeof(head)); memset(du, 0, sizeof(du)); memset(insta, 0, sizeof(insta)); memset(scc, 0, sizeof(scc)); memset(dfn, 0, sizeof(dfn)); } void add_edge(int x, int y) { edge[++num_e].to = y; edge[num_e].nex = head[x]; head[x] = num_e; } void tarjan(int u) { // tarjan求强联通分量模板 dfn[u] = low[u] = ++idx; sta[++top] = u; insta[u] = true; for (int i = head[u]; i; i = edge[i].nex) { int v = edge[i].to; if (!dfn[v]) { tarjan(v); low[u] = min(low[u], low[v]); } else if (insta[v]) low[u] = min(low[u], dfn[v]); } if (dfn[u] == low[u]) { ++tot; do { scc[sta[top]] = tot; sz[tot]++; insta[sta[top]] = false; } while (sta[top--] != u); } } int main() { int n, m; while (~scanf("%d %d", &n, &m)) { init(); for (int i = 0, u, v; i < m; i++) { scanf("%d %d", &u, &v); add_edge(u, v); } for (int i = 1; i <= n; i++) { if (!dfn[i]) tarjan(i); } for (int u = 1; u <= n; u++) { for (int i = head[u]; i; i = edge[i].nex) { int v = edge[i].to; if (scc[u] != scc[v]) du[scc[u]]++; } } int ans = 0; for (int i = 1; i <= tot; i++) { if (!du[i]) { if (ans) { ans = 0; break; } ans = i; } } printf("%d\n", ans ? sz[ans] : 0); } return 0; }
作者:_kangkang
本文版权归作者和博客园共有,欢迎转载,但必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。