原題鏈接
解題思路
最強連通子圖[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;
}