萌新刚学 OI,求助 tarjan
查看原帖
萌新刚学 OI,求助 tarjan
453406
白简楼主2022/11/25 11:29
#include<iostream>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<cstring>
#define MAXN 10500
#define MAXM 550
#define int long long

using namespace std;

int n,m;

struct Edge{
    int next,to;
}e[MAXM<<1];

int h[MAXN],cnt;

void add(int u,int v)
{
    cnt++;
    e[cnt].next = h[u];
    e[cnt].to = v;
    h[u] = cnt;
}

int tot,dfn[MAXN],low[MAXN];
int cut[MAXN];// 记录割点  

void tarjan(int x,int father)// father 表示父节点,用于判断根节点的
{
    tot++;
    dfn[x] = low[x] = tot;
    
    int child = 0;
    // 存该节点的子树个数,用来判断根节点是否为割点

    for(int i = h[x];i;i = e[i].next)
    {
        int y = e[i].to;
        if( !dfn[y] )// 没被访问过就访问
        {
            tarjan(y,father);
            // 要传入父节点 
            low[x] = min(low[x],low[y]);
            // 更新值 
            if( low[y] >= dfn[x] && x != father )
                cut[x] = 1;// 标记为割点
                // 这里特判了不是根节点 
            
            if( x == father )
                child++;// 记录子树
        }
        low[x] = min(low[x],dfn[y]);
    }
    if( x == father  && child >= 2 )
        cut[x] = 1;
        // 是根节点并且有两个或以上的子树
        // 一定是割点 

    return ;
}

int color[MAXN];
// 记录所属点双 
int cut_num;
// 统计该点双割点数量 
int fcut;
int num,ans = 1;

void dfs(int x,int color_num)
{
    color[x] = color_num;
    fcut++;
    for(int i = h[x];i;i = e[i].next)
    {
        int y = e[i].to;
        if( color[y] != color_num && cut[y] )
        {
            cut_num++;
            color[y] = color_num;
        }
        if( color[y] == 0 )
            dfs(y,color_num);
    }
}

void clear()
{
    memset(e,0,sizeof(e));
    memset(h,0,sizeof(h));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(cut,0,sizeof(cut));
    memset(color,0,sizeof(color));
    tot = cnt = 0;
    n = 0;

}

signed main()
{
    for(int k = 1;  ; k++)
    {
        scanf("%lld",&m);
        if( m == 0 )
            break;
        
        clear();
        for(int i = 1,s,t;i <= m; i++)
        {
            scanf("%lld%lld",&s,&t);
            add(s,t);
            add(t,s);
            n = max(n,s);
            n = max(n,t);
            // 找编号最大的点当点数 
        }

        for(int i = 1;i <= n; i++)
            if( !dfn[i] )
                tarjan(i,i);
                // 求割点 
        
        for(int i = 1,color_num = 0/*类似缩点,连通块数量*/;i <= n; i++)
        {
            if( !cut[i] && !color[i] )
            {
                color_num++;
                fcut = cut_num = 0;
                dfs(i,color_num);

                if( cut_num == 0 )
                {
                    // 如果没有割点
                    num += 2;
                    // 建两个出口
                    // 要保证至少一个出口不塌 
                    ans *= (fcut-1)*fcut/2;
                }
                if( cut_num == 1 )
                {
                    // 有一个割点 
                    num++;
                    // 建一个出口,但不能建在割点上 
                    // 如果割点塌了
                    ans *= fcut;
                }
            }
        }

        printf("Case %lld: %lld %lld\n",k,num,ans);
    }
    return 0;
}
2022/11/25 11:29
加载中...