[JSOI2010] 连通数 - 强连通分量,缩点
阅读原文时间:2023年07月14日阅读:1

复习一下手工 tarjan

#include <bits/stdc++.h>
using namespace std;

vector <int> g[2005],scc[2005];
int ind,f[2005],siz[2005],dfn[2005],low[2005],vis[2005],s[2005],bel[2005],top,tot,n,m,t1,t2,t3;
char ch[2005];
void dfs(int p) {
    vis[p]=1;
    s[++top]=p;
    dfn[p]=low[p]=++ind;
    for(int i=0;i<g[p].size();i++) {
        int q=g[p][i];
        if(vis[q]==0) dfs(q), low[p]=min(low[p],low[q]);
        else if(vis[q]==1) low[p]=min(low[p],dfn[q]);
    }
    if(dfn[p]==low[p]) {
        ++tot;
        int q=s[top--];
        while(q && q-p) {
            scc[tot].push_back(q);
            bel[q]=tot;
            q=s[top--];
        }
        if(q) {
            scc[tot].push_back(q);
            bel[q]=tot;
            --top;
        }
    }
    vis[p]=2;
}

vector <int> G[2005];

int main() {
    cin>>n;
    for(int i=1;i<=n;i++) {
        cin>>ch+1;
        for(int j=1;j<=n;j++) {
            if(ch[j]=='1') g[i].push_back(j);
        }
    }
    for(int i=1;i<=n;i++) {
        if(vis[i]==0) {
            dfs(i);
        }
    }
    for(int i=1;i<=tot;i++) {
        for(int j=0;j<scc[i].size();j++) {
            G[i].push_back(bel[scc[i][j]]);
        }
    }
    for(int i=1;i<=tot;i++) {
        siz[i]=scc[i].size();
        for(int j=0;j<G[i].size();j++)
            if(i-G[i][j]) siz[i]+=siz[G[i][j]];
    }
    int ans = 0;
    for(int i=1;i<=tot;i++) ans+=scc[i].size()*siz[i];
    cout<<ans<<endl;
}