[BZOJ 2730]HNOI 2012鉱場構築|割点

5851 ワード

まず最低数が明らかに1より大きい場合、1つの点を爆破することを考慮し、それが割点でなければ影響がなく、割点であれば爆破後の各連通ブロックに少なくとも1つの救援点を満たさなければならない.この図に割点がなければ2つの点を選んで置けばいいのですが、答えはC(n,2)です.そうでなければ、各割点を求め、この図を割点によっていくつかの連通ブロックに分割します.これらの連通ブロックを縮点するのは木の構造です.木の上のいずれかの辺が切れた後に分かれた2つのブロックに救援点があるようにするには、必ず各葉ブロックに救援点を置きます.すべての葉ブロックのsizeを乗せればいいです.
#include
#include
#include
#define N 1005
#define ll unsigned long long
#define clr(a) memset(a,0,sizeof(a))
#define t1 st[top]
using namespace std;
struct edge{
    int e,next;
}ed[N*2];
int n,m,i,j,ans1,ne,s,e,tim,ecc,cnt,a[N],dfn[N],low[N],size,cut[N];
ll ans2;
void add(int s,int e)
{
    ed[++ne].e=e;ed[ne].next=a[s];a[s]=ne;
}
void tarjan(int x,int fa)
{
    dfn[x]=low[x]=++tim;
    int cnt1=0;
    bool f=false;
    for (int j=a[x];j;j=ed[j].next)
        if (ed[j].e!=fa)
        {
            if (!dfn[ed[j].e]) tarjan(ed[j].e,x),cnt1++;
            low[x]=min(low[x],low[ed[j].e]);
            if (dfn[x]<=low[ed[j].e]) f=true;
        }
    if ((fa==-1&&cnt1>1)||(fa!=-1&&f)) cut[x]=1,cnt++;
}
void dfs(int x,int tim)
{
    dfn[x]=1;size++;
    for (int j=a[x];j;j=ed[j].next)
    {   
        if (cut[ed[j].e]&&dfn[ed[j].e]if (!dfn[ed[j].e]) dfs(ed[j].e,tim);
    }
}
int main()
{
    freopen("2730.in","r",stdin);
    freopen("2730.out","w",stdout);
    for (int t=1;;t++)
    {
        scanf("%d",&m);
        if (!m) return 0;
        n=ne=tim=ans1=cnt=0;
        clr(a);clr(dfn);clr(cut);
        for (i=1;i<=m;i++)
        {
            scanf("%d%d",&s,&e);
            add(s,e);add(e,s);
            n=max(n,max(s,e));
        }
        for (i=1;i<=n;i++) if (!dfn[i]) tarjan(i,-1);
        ans2=1ll;
        clr(dfn);
        if (cnt==0) ans1=2,ans2=(n*(n-1)/2);
        else 
            for (i=1;i<=n;i++)
            {
                cnt=0;
                if (!cut[i]&&!dfn[i]) size=0,dfs(i,i);
                if (cnt==1) ans2*=size,ans1++;
            }
        printf("Case %d: %d %llu
"
,t,ans1,ans2); } }