> 原题来源***P2863 [USACO06JAN]***The Cow Prom S
题目描述 有一个 nn 个点,mm 条边的有向图,请求出这个图点数大于 11 的强联通分量个数。
输入格式 第一行为两个整数 nn 和 mm。
第二行至 m+1m+1 行,每一行有两个整数 aa 和 bb,表示有一条从 aa 到 bb 的有向边。
输出格式 仅一行,表示点数大于 11 的强联通分量个数。
输入输出样例
输入 #1复制
5 4
2 4
3 5
1 2
4 1
输出 #1复制
1
思路: 该题就是tarjan算法找出所有强连通分量, 然后再来一个桶, 记录大于二的强连通分量,输出即可, 关键代码必须要do{}while, 直接while(…)会丢是数据, 但是样例可以过, 被这里卡了很久,很久, 一开始就是没有注意!!!!!!!!!
if(low[u] == dfn[u]){
key ++;
int v;
do{
num[key] ++;
v = st.top();
st.pop();
in[v] = false;
}while(u != v) ;
}
题解
/*
https://www.luogu.com.cn/problem/P2863
P2863 [USACO06JAN]The Cow Prom S
*/
#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e4 + 10;
int n, m;
vector<int > g[maxn];
int low[maxn];
int dfn[maxn];
bool in[maxn];
int num[maxn];
stack<int > st;
int cnt;
int key;
void tarjan(int u){
low[u] = dfn[u] = ++ cnt;
st.push(u);
in[u] = true;
int sz = g[u].size();
for(int i = 0; i < sz; i ++){
int v = g[u][i];
if(!dfn[v]){
tarjan(v);
low[u] = min(low[u], low[v]);
}else if(in[v]){
low[u] = min(low[u], dfn[v]);
}
}
if(low[u] == dfn[u]){
key ++;
int v;
do{
num[key] ++;
v = st.top();
st.pop();
in[v] = false;
}while(u != v) ;
}
}
int main(){
cin >> n >> m;
for(int i = 1; i <= m; i ++){
int a, b;
cin >> a >> b;
g[a].push_back(b);
}
for(int i = 1; i <= n; i ++){
if(!dfn[i]){
tarjan(i);
}
}
int ans = 0;
for(int i = 1; i <= key; i++){
cout << num[i] << endl;
if(num[i] > 1){
ans ++;
}
}
cout << ans << endl;
return 0;
}