P2341 受欢迎的牛
由于喜欢是单向的,同时具有传递性,所以会想到图论中的强连通分量。
然后使用 Tarjan 缩点,原图变成一张 DAG,问题转化为寻找能被所有缩点到达的分量。
发现如果某个 SCC 要被所有的 SCC 到达,那么它一定没有出度。而若 DAG 中有两个或更多的出度为 0 的汇点,它们之间无法相互抵达,就不存在要求的奶牛了。
反过来,若缩点 DAG 只有一个汇点,从任意节点沿出边不断前进,最终只能停在这个汇点,因此所有节点都会到达它。答案就是这个唯一零出度 SCC 的大小,否则答案为 0。
不过这题不需要显式建立缩点图,只要统计每个 SCC 的出度即可。Tarjan 和出度统计的总时间复杂度为 O(N+M),空间复杂度同样为 O(N+M)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| struct Edge { int v; ll w; }; vector<Edge> adj[maxn];
int dfn[maxn], low[maxn], timer; int scc[maxn], scnt; int scc_siz[maxn];
int stk[maxn], top; bool ins[maxn];
void tarjan( int u ) { dfn[u] = low[u] = ++ timer; stk[++ top] = u, ins[u] = true;
for( auto [v, w] : adj[u] ) { if( !dfn[v] ) { tarjan( v ); low[u] = min( low[u], low[v] ); } else if( ins[v] ) { low[u] = min( low[u], dfn[v] ); } }
if( low[u] == dfn[u] ) { scnt ++; int v; do { v = stk[top --]; ins[v] = 0; scc[v] = scnt; scc_siz[scnt] ++; } while( v != u ); } }
int oud[maxn];
void solve( ) { int n = read( ), m = read( ); for( int i = 1; i <= m; ++ i ) { int u = read( ), v = read( ); adj[u].push_back({ v, 0 }); }
for( int i = 1; i <= n; ++ i ) { if( !dfn[i] ) tarjan( i ); }
for( int u = 1; u <= n; ++ u ) { for( auto [v, w] : adj[u] ) { if( scc[u] != scc[v] ) oud[scc[u]] ++; } }
int cnt = 0, ans = 0; for( int i = 1; i <= scnt; ++ i ) { if( oud[i] == 0 ) { cnt ++; ans = scc_siz[i]; } } if( cnt == 1 ) cout << ans << '\n'; else cout << 0 << '\n'; }
|
记录
- 归属:SCC 缩点;
- 新增:被所有点到达的节点恰好组成缩点 DAG 的唯一汇点 SCC,因此只需判断零出度分量是否唯一。
- 分类:待定