[BZOJ 2730][HNOI2012]矿场搭建(Tarjan)

Description

煤矿工地可以看成是由隧道连接挖煤点组成的无向图。为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处。于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口。请写一个程序,用来计算至少需要设置几个救援出口,以及不同最少救援出口的设置方案总数。

Solution

求割点,把所有割点去掉后形成的某个连通块如果与两个及以上割点相连,就可以不建救援出口,否则要建

方案就是这些要建救援出口的连通块的大小乘积(乘法原理)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define MAXN 505
typedef long long LL;
using namespace std;
int n,m,head[MAXN],cnt,dfn[MAXN],low[MAXN],dfn_clock,tot,num;
bool cut[MAXN],vis1[MAXN],vis2[MAXN];
struct Node
{
    int next,to;
}Edges[MAXN*2];

void init()
{
    memset(head,-1,sizeof(head));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(cut,0,sizeof(cut));
    memset(vis1,0,sizeof(vis1));
    m=dfn_clock=cnt=0;
}
void addedge(int u,int v)
{
    Edges[++cnt].next=head[u];
    head[u]=cnt;
    Edges[cnt].to=v;
}
void tarjan(int u,int f)
{
    low[u]=dfn[u]=++dfn_clock;
    int child=0;
    for(int i=head[u];~i;i=Edges[i].next)
    {
        int v=Edges[i].to;
        if(!dfn[v])
        {
            child++;
            tarjan(v,u);
            low[u]=min(low[u],low[v]);
            if(low[v]>=dfn[u])cut[u]=1;
        }
        else if(v!=f)
        low[u]=min(low[u],dfn[v]);
    }
    if(!f&&child==1)cut[u]=0;
}
void dfs(int u)
{
    vis1[u]=1;
    for(int i=head[u];~i;i=Edges[i].next)
    {
        int v=Edges[i].to;
        if(vis1[v])continue;
        if(!cut[v]) num++,dfs(v);
        else if(!vis2[v])vis2[v]=1,tot++;
    }
}
int main()
{
    int kase=0;
    while(~scanf("%d",&n)&&n)
    {
        ++kase;
        init();
        for(int i=1;i<=n;i++)
        {
            int s,t;
            scanf("%d%d",&s,&t);
            addedge(s,t);
            addedge(t,s);
            m=max(m,s),m=max(m,t);
        }
        LL ans1=0,ans2=1;
        for(int i=1;i<=m;i++)if(!dfn[i])tarjan(i,0);
        for(int i=1;i<=m;i++)
        {
            if(!vis1[i]&&!cut[i])
            {
                memset(vis2,0,sizeof(vis2));
                tot=0;num=1;
                dfs(i);
                if(tot==1)ans1++,ans2*=num;
            }
        }
        if(!ans1)ans1=2,ans2=1LL*m*(m-1)/2;
        printf("Case %d: %lld %lld
",kase,ans1,ans2);
    }
    return 0;
} 
原文地址:https://www.cnblogs.com/Zars19/p/6736223.html