HNOI2012 矿场搭建 v-DCC缩点+分类讨论

HNOI2012 矿场搭建

题目传送

sol:

首先需要对v-DCC缩点,对于缩点后的每一个连通块需分类讨论一下。

对于每一个连通块(v-DCC):

1、不存在割点。需要建两个出口(毁掉一个还得有一个)。

2、存在一个割点。需要一个出口。(毁掉割点,走连通块内出口;毁掉连通块内出口,走割点到别的连通块出口)。

3、存在至少两个割点。不需要出口。(不管毁掉哪里,都能到别的连通块出口)。

方案的话就是把各个连通块的方案乘起来就好了。

#include<bits/stdc++.h>
#define IL inline
#define RG register
#define DB double
#define LL long long
using namespace std;

const int N=505;

LL ans2;
int n,m,RT,cnt,tot,top,ans1,Time,c[N],sta[N],low[N],dfn[N],cut[N],sum[N],head[N],eDcc[N][N];

struct EDGE{int next,to;}e[N<<1];

IL int gi() {
    RG int x=0,p=1; RG char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-') p=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=x*10+(ch^48),ch=getchar();
    return x*p;
}

IL void New_case() {
    n=tot=cnt=top=Time=0;
    memset(c,0,sizeof(c));
    memset(&e,0,sizeof(e));
    memset(head,0,sizeof(head));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(cut,0,sizeof(cut));
    memset(sum,0,sizeof(sum));
}
 
IL void make(int a,int b) {
    e[++tot]=(EDGE){head[a],b},head[a]=tot;
    e[++tot]=(EDGE){head[b],a},head[b]=tot;
}

void Tarjan(int x) {
    RG int i,k,y,num=0;
    dfn[x]=low[x]=++Time,sta[++top]=x;
    for(i=head[x];i;i=e[i].next)
        if(!dfn[y=e[i].to]) {
            Tarjan(y),++num,low[x]=min(low[x],low[y]);
            if(low[y]>=dfn[x]) {
                if(x!=RT||num>1) cut[x]=1;
                ++cnt;
                do{
                    k=sta[top--];
                    eDcc[cnt][++c[cnt]]=k;
                }while(k!=y);
                eDcc[cnt][++c[cnt]]=x;
            }
        }
        else low[x]=min(low[x],dfn[y]);
}

int main()
{
    RG int i,j,x,y,Case=0;
    while(scanf("%d",&m)!=EOF&&m) {
        New_case();
        for(i=1;i<=m;++i)
            x=gi(),y=gi(),n=max(n,max(x,y)),make(x,y);
        for(i=1;i<=n;++i)
            if(!dfn[i]) RT=i,Tarjan(i);
        for(i=1;i<=cnt;++i)
            for(j=1;j<=c[i];++j) sum[i]+=cut[eDcc[i][j]];
        for(i=1,ans1=0,ans2=1;i<=cnt;++i)
            if(!sum[i]) ans1+=2,ans2*=c[i]*(c[i]-1)/2;
            else if(sum[i]==1) ++ans1,ans2*=c[i]-1;
        printf("Case %d: %d %lld\n",++Case,ans1,ans2);
    }
    return 0;
}
上一篇:[2021.8集训Day1/JZOJ.4250]【五校联考7day1附加题】路径/洛谷P4799 [CEOI2015 Day2]世界冰球锦标赛


下一篇:optimize table tablename