2730: [HNOI2012]矿场搭建 tarjan求割点

又做了一道求割点的题。
可以发现,如果boom处不在割点那么是没有影响的,所以我们只讨论割点的情况。
在求出所有的割点之后,整个图被分成了几个联通块。我们对联通块含有割点的个数进行讨论。
如果无割点,那么我们只需要建立两个出口,防止建立一个出口而这个出口炸掉。
如果有一个割点,我们必须建立一个出口(不能在割点处)。
如果有两个割点,我们不需要建立出口,因为一个爆炸时我们可以从另一个出去。
所以先tarjan求割点再dfs一下每个联通块就好了。

一开始vis数组弄了bool类型然而我用的是时间戳2333333333333。

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
int n,m,cnt,num,tot,deg,ans1,T,cases,root;
ll ans2;
int head[505],dfn[505],low[505],vis[505];
bool cut[505];
int next[1005],list[1005];
inline int read()
{
    int a=0,f=1; char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
    while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
    return a*f;
}
inline void insert(int x,int y)
{
    next[++cnt]=head[x];
    head[x]=cnt;
    list[cnt]=y;
}
void tarjan(int x,int f)
{
    dfn[x]=low[x]=++tot;
    for (int i=head[x];i;i=next[i])
        if (!dfn[list[i]]) 
        {
            tarjan(list[i],x);
            low[x]=min(low[x],low[list[i]]);
            if (low[list[i]]>=dfn[x]) 
            {
                if (x==root) deg++; else cut[x]=1;
            }
        }
        else if (list[i]!=f) low[x]=min(low[x],dfn[list[i]]);
}
void dfs(int x)
{
    vis[x]=T;
    if (cut[x]) return;
    cnt++;
    for (int i=head[x];i;i=next[i])
    {
        if (cut[list[i]]&&vis[list[i]]!=T) num++,vis[list[i]]=T;
        if (!vis[list[i]]) dfs(list[i]);
    }
}
int main()
{
    m=read();
    while (m)
    {
        memset(head,0,sizeof(head));
        memset(dfn,0,sizeof(dfn));
        memset(low,0,sizeof(low));
        memset(cut,0,sizeof(cut));
        memset(vis,0,sizeof(vis));
        cnt=tot=n=ans1=T=0; ans2=1;
        for (int i=1;i<=m;i++)
        {
            int u=read(),v=read();
            n=max(n,max(u,v));
            insert(u,v); insert(v,u);
        }
        for (int i=1;i<=n;i++)
        {
            if (!dfn[i]) tarjan(root=i,0);
            if (deg>=2) cut[root]=1;
            deg=0;
        }
        for (int i=1;i<=n;i++)
            if (!vis[i]&&!cut[i])
            {
                T++; cnt=num=0; 
                dfs(i);
                if (!num) ans1+=2,ans2*=cnt*(cnt-1)/2;
                if (num==1) ans1++,ans2*=cnt;
            }
        printf("Case %d: %d %lld\n",++cases,ans1,ans2);
        m=read();
    }
    return 0;
}       

你可能感兴趣的:(2730: [HNOI2012]矿场搭建 tarjan求割点)