原题链接
解题思路
最强连通子图[Tarjan算法]
dfn[u]为节点u的次序编号(时间戳)
low[u[为u或u的子树能够追溯到的最早的栈中节点的次序号
当dfn[u]=low[u]时,以u为根的搜索子树上的所有节点是一个强连通分量
伪代码
tarjan(u){
dfn[u] = low[u] = ++index;//时间戳
s.push(u);
for each(u,v) in E
if (v is not visited)//节点v未被访问过
targan(v)
low[u] = min(low[u],low[v]);
else if(v in s) //节点v还在栈中
low[u] = min(low[u],dfn[v]);
if dfn[u] == low[u] //节点u是强连通分量的跟
repeat
v = s.pop //将v进行退栈 ,为改连通分量的一个顶点
print v
until u == v
}
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 10010;
vector<int> graph[maxn];
bool ins[maxn];//标记是否在栈中
int dfn[maxn],low[maxn];
int ans=0;
int index = 0;
stack<int> s;
void tarjan(int u){
dfn[u] = low[u] = ++index;//时间戳
ins[u] = true;
s.push(u);
for(int i=0;i<graph[u].size();i++){
int v = graph[u][i];
if(dfn[v]==0){
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]){
//出栈
int cnt = 0,t;
do{
t = s.top();
s.pop();
ins[t] = false;
cnt ++;
}while(t != u);
ans += cnt*(cnt-1)/2;
}
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++){
int a,b;
scanf("%d%d",&a,&b);
graph[a].push_back(b);
}
for(int i=1;i<=n;i++){
if(dfn[i]==0){
tarjan(i);
}
}
cout<<ans;
return 0;
}