- 连通分量
受欢迎的牛
- @ 2025-10-1 10:39:37
#include<bits/stdc++.h>
using namespace std;
const int N = 10010, M = 50010;
int n, m;
int h[N], e[M], ne[M],idx;
int dfn[N], low[N], timestamp;
int stk[N], top;
bool in_stk[N];
int id[N], scc_cnt, Size[N];
int d[N];
void add(int a, int b)
{
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
void tarjan(int u)
{
dfn[u] = low[u] = ++ timestamp;
stk[++top] = u, in_stk[u] = true;
for(int i = h[u]; i != -1; i = ne[i])
{
int j = e[i];
if(!dfn[j]) //j没有被遍历过
{
tarjan(j);
low[u] = min(low[u], low[j]);
}
else if(in_stk[j])
{
low[u] = min(low[u], dfn[j]);
}
}
if(dfn[u] == low[u])
{
int y;
++ scc_cnt;
do
{
y = stk[top --];
in_stk[y] = false;
id[y] = scc_cnt;
Size[scc_cnt] ++;
}while(y != u);
}
}
int main()
{
cin >> n >> m;
memset(h, -1, sizeof h);
while(m --)
{
int a, b;
cin >> a >> b;
add(a, b);
}
for(int i = 1; i <= n; i++)
if(!dfn[i]) tarjan(i);
//缩点后的出度
for(int i = 1; i <= n; i++)
for(int j = h[i]; j != -1; j = ne[j])
{
int k = e[j];
int a = id[i], b = id[k]; //a, b为点i,k所在连通分量的编号
if(a != b) d[a] ++;
}
int sum = 0, ans = 0;
for(int i = 1; i <= scc_cnt; i++)
{
if(d[i] == 0)
{
sum ++;
ans += Size[i];
if(sum > 1) {
ans = 0;
break;
}
}
}
cout << ans;
return 0;
}
/*
有向图的连通分量: 对于一个有向图,其中分量中任意两点u和v,必然可以从u走到v,且从v走到u
强连通分量:极大连通分量
tarjan算法求强连通分量(SCC)
实现方式:DFS
对于每个点定义两个时间戳:
dfn[u]:表示遍历到u这个点的时间戳
low[u]:从u开始走,所能遍历到的最小时间戳是什么
low[u] == dfn[u] 等价为 u是其所在的强连通分量的最高点
四种边:
1.树枝边(x,y): x是y的父节点 x->y dfn[y] > dfn[x] low[u] > dfn[u]
2.前向边(x,y): x是y的祖先节点 x->...->y dfn[y] > dfn[x] low[u] > dfn[u]
3.后向边(x,y): x是y的祖先节点,y->x dfn[x] > dfn[y] 后向边终点 dfn[u]==low[u]
4.横叉边(x,y): x是访问过的点 dfn[x] > dfn[y]
常用场景: 缩点
连通分量编号递减的顺序一定拓扑序
对于本题:
当一个强连通分量的出度为0,则该强连通分量的所有牛都被其他强连通分量的牛欢迎
*/
0 条评论
目前还没有评论...