写博客的好处之一就是能方便的找模板
这里介绍的是Tarjan写法。
维护两个数组, dfn[], low[]。对图进行dfs,dfn表示当前节点的时间戳, low表示以当前节点为子树中最小的时间戳。
- dfs访问每个节点,并将节点压入栈中,访问它的子节点。
- 子节点在栈中更新节点的low
- 子节点不在栈中递归访问子节点回溯更新low
判断一个联通分量的标志:dfn[u] = low[u]
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <stack>
#define mem(a, b) memset(a, b, sizeof(a))
using namespace std;
const int maxn = 1e5 + 10;
int dfn[maxn], low[maxn];
int cnt, tot, n, m;
int head[maxn];
int belong[maxn];
bool vis[maxn];
int ans;
stack<int>st;
struct node{
int v, nex;
}edge[maxn * 2];
void init()
{
while(!st.empty())
st.pop();
mem(dfn, 0), mem(low, 0);
mem(vis, 0);
mem(belong, 0);
mem(head, -1);
cnt = 0;
ans = 0;
tot = 0;
}
void addedge(int u, int v)
{
edge[cnt].v = v;
edge[cnt].nex = head[u];
head[u] = cnt++;
}
void tarjan(int u)
{
dfn[u] = low[u] = ++tot;
vis[u] = 1;
st.push(u);
for(int i = head[u]; i != -1; i = edge[i].nex)
{
int v = edge[i].v;
if(!dfn[v])
{
tarjan(v);
low[u] = min(low[u], low[v]);
}
else if(vis[v])
{
low[u] = min(low[u], dfn[v]);
}
}
if(dfn[u] == low[u])
{
int x;
ans++;
while(1)
{
x = st.top();
st.pop();
vis[x] = 0;
if(x == u)
break;
}
}
}
int main()
{
while(cin >> n >> m)
{
if(n == 0 && m == 0)
break;
init();
for(int i =1; i <= m; i++)
{
int x, y;
cin >> x >> y;
addedge(x, y);
}
for(int i = 1; i <= n; i++)
if(dfn[i] == 0)
tarjan(i);
cout << ans << " ";
if(ans == 1)
cout << "Yes" << endl;
else
cout << "No" << endl;
}
return 0;
}