校园网络(tarjan)

一眼题,要跑强连通分量

那就乖乖的打一边tarjan了,然后该怎么办呢

记录缩点后的图,并记录这个图上每个点的入度和出度

因为,如果一个点入度为零,那么就说明没有任何点跟他连接,则必须把它当成母点

反之如果出度为零,证明这个点没有办法跑出去,那么就需要当成母点,记录即可

比较答案即可

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+7;
const int M=5e7+6;
int m,n,k,cnt,ans;
int dfn[N],low[N],head[N],del[N],tot,color[N],sum[N],num,in[N],out[N],inc;
struct edge
{
    int nx,to;
} e[M];
void add_edge(int a,int b)
{
    cnt++;e[cnt].nx=head[a];e[cnt].to=b;head[a]=cnt;
}
bool vis[N];
stack<int> s;
void paint(int x)
{
    s.pop();
    color[x]=num;
    sum[num]++;
    vis[x]=false;
}
void tarjan(int x)
{
    dfn[x]=low[x]=++tot;
    vis[x]=true;s.push(x);
    for (int i=head[x];i;i=e[i].nx)
    {
        int y=e[i].to;
        if (!dfn[y]) 
        {
            tarjan(y);
            low[x]=min(low[x],low[y]);
        }
        else if (vis[y]) low[x]=min(low[x],dfn[y]);
    }
    if (dfn[x]==low[x])
    {
        num++;
        while (s.top()!=x)
        {
            int t=s.top();
            paint(t);
        }
        paint(x);
    }
}
int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
    {
        while (true)
        {
            int x;
            scanf("%d",&x);
            if (x==0) break;
            add_edge(i,x);
        }
    }
    for (int i=1;i<=n;i++)
    if (!dfn[i]) tarjan(i);
    for (int i=1;i<=n;i++)
     for (int j=head[i];j;j=e[j].nx)
     {
         int y=e[j].to;
         if (color[i]==color[y]) continue;
        in[color[y]]++;
        out[color[i]]++;
     }
    for (int i=1;i<=num;i++)
    if (!in[i]) ans++;
    for (int i=1;i<=num;i++)
    if (!out[i]) inc++;
    printf("%d
",ans);
    if (num==1) printf("%d",0);
    else printf("%d",max(ans,inc));
    return 0;
} 
慢即是快,细则是能,于小处铸迤逦
原文地址:https://www.cnblogs.com/Hale522520/p/10628390.html