题目传送门:【POJ 3180】
题目大意:给你 N 个点和 M 条边,求大小 >1 的强连通分量的个数。(省略多余描述)(2 ≤ N ≤ 10000,1 ≤ M ≤ 50000)
题目分析:
题目都描述到这么明显的地步了,直接用 Tarjan 或 Kosaraju 求强连通分量就可以了,求解的同时维护每个强连通分量的大小,最后输出大小 >1 的强连通分量的个数即可。
下面附上 Tarjan 的代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<stack>
using namespace std;
const int MX = 200005;
const int INF = 0x3f3f3f3f;
struct Edge{
int to,len,next;
};
Edge edge[MX];
int low[MX],dfn[MX],index = 0,ss[MX],belong[MX],tot = 0,cnt = 0;
int head[MX],now = 0,n,m;
stack<int> s; //belong:每个点属于的强连通分量的编号
bool ins[MX]; //是否已入栈
void adde(int u,int v,int l){
edge[++now].to = v;
edge[now].len = l;
edge[now].next = head[u];
head[u] = now;
}
void tarjan(int u){
low[u] = dfn[u] = ++index;
s.push(u);
ins[u] = true;
for (int i = head[u];i;i = edge[i].next){
int v = edge[i].to;
if (!dfn[v]){
tarjan(v);
if (low[v] < low[u]){
low[u] = low[v];
}
} else if (ins[v] && dfn[v] < low[u]){
low[u] = dfn[v];
}
}
if (low[u] == dfn[u]){
cnt++;
int tmp;
do{
tmp = s.top();
s.pop();
belong[tmp] = cnt;
ins[tmp] = false;
}while (tmp != u);
}
}
int main(){
int a,b;
cin>>n>>m;
for (int i = 1;i <= m;i++){
cin>>a>>b;
adde(a,b,1);
}
for (int i = 1;i <= n;i++){
if (!dfn[i]) tarjan(i);
}
for (int i = 1;i <= n;i++){ //ss:统计每个强连通分量的大小
ss[belong[i]]++;
}
for (int i = 1;i <= n;i++){
if (ss[i]>1) tot++;
}
printf("%d\n",tot);
return 0;
}