201509-4 高速公路 tarjan强连通分量算法 O(V+E)

#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<vector>
#include<string>
#include<limits.h>
#include<cmath>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<functional>
#define iter(i,start,end) for(int (i)=(start);(i)<(end);(i)++)
using namespace std;

const int nmax=10000+5;
int n,m;    //n城市个数\in[1,10000] m单向高速公路个数\in[1,100000]
vector<int>g[nmax];     
int groupnum,time,ans;  //groupnum强连通分量的数量(从1开始计数  time时间    ans强连通分量中点对的数量
bool onstack[nmax];     //是否在栈内
int group[nmax],low[nmax],dfn[nmax],member[nmax];    //low记录该点最早能回溯到的地方 dfn各点的时间戳   member[i]记录第i组有几个点【其实这个数组可以通过边遍历边记录ans省下来】  
stack<int> s;

void init();
void tarjan(int cur);
int main()
{
    //freopen("201509-4.txt","r",stdin);
    while(scanf("%d %d",&n,&m)==2)
    {
        init();

        while(!s.empty()) s.pop();
        time=1;
        iter(i,0,n){
            if(!dfn[i])       
                tarjan(i);
        }

        iter(i,0,n)
            member[group[i]]++;
        sort(member+1,member+groupnum+1,greater<int>());    //【记录下是不是这么写的】
        iter(i,1,groupnum+1){
            if(member[i]>1)
                ans+=member[i]*(member[i]-1)/2;
        }
        cout<<ans;
    }
    return 0;
}
void tarjan(int cur){
    low[cur]=dfn[cur]=time++;
    onstack[cur]=true;  s.push(cur);        //【记录】这几个stl的函数
    iter(i,0,g[cur].size()){
        int to=g[cur][i];
        if(!dfn[to]){    //如果遍历过就不要进去了  不然dfn和low数组都会变化的
            tarjan(to);
            if(low[to]<low[cur])        //或者直接用Min(low[to],low[cur])代替
                low[cur]=low[to];
        }
        else{
            if(onstack[to] && dfn[to] < low[cur])       //在这里wa了半天 忘了判断dfn[to] < low[cur]了
                low[cur]=dfn[to];                       //如果直接用Min(low[cur],dfn[to])代替 就不会wa这么久了...吃了没读书的亏
        }
    }

    if(low[cur]==dfn[cur])          //小环套大环也不怕
    {
        int tmp=-1,cnt=0;       //tmp赋为-1是因为节点编号从0开始的
        while(tmp!=cur){
            tmp=s.top();    s.pop();
            group[tmp]=groupnum;
            onstack[tmp]=false;
            cnt++;
        }
        groupnum++;
        if(cnt>1)   ans+=cnt*(cnt-1)/2;     //如果强连通分量只由一个数组成,也不存在什么点对之说了
    }
}

void init(){
    memset(g,0,sizeof(g));
    int u,v;
    iter(i,0,m){
        cin>>u>>v;
        g[u-1].push_back(v-1); 
    }

    groupnum=1,time=0,ans=0;
    memset(onstack,0,sizeof(onstack));
    memset(group,0,sizeof(group));      //0表示还没分组
    memset(low,0,sizeof(low));  memset(dfn,0,sizeof(dfn));
    memset(member, 0, sizeof(cout));
}

猜你喜欢

转载自blog.csdn.net/Hesy_H/article/details/82657105