//大致思路:利用vector存图,同时把反图存好,接着利用深搜把后序的序列得到
//接着是按照后序的顺序深搜反图,并且在该点被走过的前提下将图中可以一次性可以走的点放入数组
//存放 也就是用数组f[x] = y, x是每次遍历到的点,具体的值y是第一个点(找到连通图第一个点)的值,这样该图的所有强连通图都得到了
题目连接
# include <iostream>
# include <queue>
# include <string.h>
# include <vector>
# include <set>
const int maxn = 300;
bool vis[maxn];
int hou[maxn];
int f[maxn];
int n, m;
int sum_[maxn];
using namespace std;
struct node {
int to;
}temp;
int cnt = 0;
vector<node>mp[maxn];
vector<node>remp[maxn];
void dfs(int r) {
for (int i = 0;i < mp[r].size();i++) {
temp = mp[r][i];
vis[r] = 1;
if (vis[temp.to] == false) {
vis[temp.to] = true;
dfs(temp.to);
}
}
hou[cnt++] = r;//后序
}
void redfs(int r, int y) {
for (int i = 0;i < remp[r].size();i++) {
vis[r] = 0;
temp = remp[r][i];
if (vis[temp.to] == true)
{
vis[temp.to] = false;
f[temp.to] = y;//一个连通图的每一个点存放的值是该图的第一个点的值
redfs(temp.to, y);//递归位置很重要!
}
}
}
void kslj() {
for (int i = 1;i <= n;i++) {
if(vis[i]==false)
dfs(i);
}
// for (int i = 0;i < cnt;i++) {
// cout << hou[i] << " ";
//}
for (int i = cnt - 1;i >= 0;i--) {
redfs(hou[i], hou[i]);
}
}
int main() {
cin >> n >> m;
memset(vis, false, sizeof(vis));
memset(f, 0, sizeof(f));
memset(hou, 0, sizeof(hou));
while(m--) {
int x1, x2;
cin >> x1 >> x2;
temp.to = x2;
mp[x1].push_back(temp);
temp.to = x1;
remp[x2].push_back(temp);//存反图
}
kslj();
for (int i = 1;i <= n;i++) {
sum_[f[i]]++;
}
int sum = 0;
for (int i = 1;i <= n;i++) {
sum += sum_[i]*(sum_[i]-1)/2;
}
cout << sum << endl;
/*set<int>s;此处用于找到有几个强连通图
for (int i = 1;i <= n;i++) {
s.insert(f[i]);
cout << f[i] << " ";
}*/
//cout << s.size() << endl;
}
以下是学习代码(有问题的)
#include <iostream>
#include <vector>
#include <string.h>
#define _ ios_base::sync_with_stdio(0),cin.tie(0)
using namespace std;
struct Edge{
int to;
}edge; //作为临时的储存边的变量
const int maxn=305;
vector<Edge> mp[maxn];//放图
vector<Edge> remp[maxn];//放反图
int n,m;
bool vis[maxn];//标记是否走过 以及第一遍已经走过了
int f[maxn];//下标是访问节点,具体数值是第一次访问节点的值,从而确定强连通有几个
int p[maxn];//后序
int cnt=0;
void dfs(int x){
vis[x] = 1;
for(int i=0;i<mp[x].size();i++){
edge=mp[x][i];
int v=edge.to;
if(!vis[v]){//当一次都没有访问过进入循环
vis[v]=true;//进入后改变
dfs(v);
}
}
p[cnt++]=x;
}
void redfs(int x,int y){//此处的y是一个强连通图中进入的第一个结点,
for(int i=0;i<remp[x].size();i++){
vis[x] = 0;
edge=remp[x][i];
int v=edge.to;
if(vis[v]){//当被走过一遍的点可直接进入下一次便利(所有点在第一遍都走过),
vis[v]=false;
f[v]=y;//缩点,该图的所有点的值都是该图进入的第一个数
redfs(v,y);
}
}
}
void kosaraju(){
for(int i=1;i<=n;i++){//保证所有点都不访问
if(!vis[i])
dfs(i);
}
// for(int i=0;i<cnt;i++)
// cout<<p[i]<<" ";
// cout<<endl;
for(int j=cnt-1;j>=0;j--){
if(vis[p[j]])//反方向访问,按后序的顺序深搜该图
redfs(p[j],p[j]);//第二个变量表示第一个访问进去的点是谁
}
}
int main(){
cin>>n>>m;
memset(vis,false,sizeof(vis));
memset(f,-1,sizeof(f));
memset(p,-1,sizeof(p));
cnt=0;
while(m--){
int u,v;
cin>>u>>v;
edge.to=v;
mp[u].push_back(edge);
edge.to=u;
remp[v].push_back(edge);
}
kosaraju();
/*求对点个数,强连通图中的边数*/
vector<int>ans(n+1);
for(int i=1;i<=n;i++){
ans[f[i]]++;//数组中放一个强连通图中有几个点数
}
int sum=0;
for(int i=1;i<=n;i++){
sum+=(ans[i]*(ans[i]-1))/2;//根据连通图的点数确实一个连通图有多少边
// cout<<ans[i]<<endl;
}
cout<<sum;
/*查找强连通分量的个数 set<int>ans;
for(int i=1;i<=n;i++){
ans.insert(f[i]);
}
cout<<ans.size()<<endl;
*/
return 0;
}
/*
9 12
1 2
1 4
2 3
3 1
4 5
5 6
5 7
5 8
6 4
7 6
8 9
9 8
*/