问题描述
某国有n个城市,为了使得城市间的交通更便利,该国国王打算在城市之间修一些高速公路,由于经费限制,国王打算第一阶段先在部分城市之间修一些单向的高速公路。
现在,大臣们帮国王拟了一个修高速公路的计划。看了计划后,国王发现,有些城市之间可以通过高速公路直接(不经过其他城市)或间接(经过一个或多个其他城市)到达,而有的却不能。如果城市A可以通过高速公路到达城市B,而且城市B也可以通过高速公路到达城市A,则这两个城市被称为便利城市对。
国王想知道,在大臣们给他的计划中,有多少个便利城市对。
输入格式
输入的第一行包含两个整数n, m,分别表示城市和单向高速公路的数量。
接下来m行,每行两个整数a, b,表示城市a有一条单向的高速公路连向城市b。
输出格式
输出一行,包含一个整数,表示便利城市对的数量。
样例输入
5 5
1 2
2 3
3 4
4 2
3 5
样例输出
3
样例说明
城市间的连接如图所示。有3个便利城市对,它们分别是(2, 3), (2, 4), (3, 4),请注意(2, 3)和(3, 2)看成同一个便利城市对。
思路: 就是找强联通分量,然后简单计算。
我之前脑子里只有一个方法, 的复杂度。
Tarjan算法: (塔尔杨算法)
强联通分量也就是存在一个环,将这些点连通起来,所以我们的目的就是找到这个环,然后将环上的点标记。
两个数组:
表示结点
的时间戳,也就是从根节点开始 第几个结点访问 结点
的。
: 表示结点
子节点中 最小的时间戳
1.如果结点 的
和
不一样,说明当前结点处在某一个连通分量中。
2.如果上述两个值相等,说明当前结点下的子树,确实是一颗树 或者是连通分量的根节点,不存在联通分量,可以删除了,
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i,a,b) for(int i=a;i<b;++i)
const int N=10010;
vector<int>edge[N];
int in[N];
int stk[N],top=-1;
int n,m;
int dfn[N],low[N];
int cntT=0;
int ans=0;
void Tarjan(int u){
dfn[u]=low[u]=++cntT;
stk[++top]=u;
in[u]=1;
rep(i,0,edge[u].size()){
int v=edge[u][i];
if(dfn[v]==0){
Tarjan(v);
low[u]=min(low[u],low[v]);
}else if(in[v]){
low[u]=min(low[u],dfn[v]);
}
}
if(dfn[u]==low[u]){
int sum=0;
do{
in[stk[top]]=0;
top--;
sum++;
//printf("%d\n",stk[top+1]);
}while(stk[top+1]!=u);
ans=ans+sum*(sum-1)/2;
}
}
int main(){
scanf("%d %d",&n,&m);
int u,v;
rep(i,0,m){
scanf("%d %d",&u,&v);
edge[u].push_back(v);
}
rep(i,1,n+1){
if(dfn[i]==0){
Tarjan(i);
}
}
printf("%d\n",ans);
return 0;
}
参考: