【luogu3225】 [HNOI2012]矿场搭建 [tarjan 割点]

P3225 [HNOI2012]矿场搭建

好吧 我是看了yyb大佬的题解才做起的 并且把我的割点模板改得和他的一样

先找割点 然后再一个点一个点地来找连通块 统计该块里的割点数和非割点数

如果没有割点,分类讨论:
1.只有1个节点,只需要建立1个出口,方案累乘不变
2.有n个节点(n≥2),至少建立两个出口,一个坍塌了走另一个,方案累乘数(n-1)*n/2,由乘法原理得到。
有且仅有1个割点:需要建立一个出口,割点坍塌了走出口,出口坍塌了走割点到达别的连通块
有2个及2个以上个割点:不用建立出口,割点坍塌了1个可以直接到达其他的割点。

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<cmath>
#include<stack>
#include<algorithm>
using namespace std;
#define ll long long
#define rg register
#define lson o<<1
#define rson o<<1|1
const int N=500+5,M=500000+5,inf=0x3f3f3f3f,P=19650827;
int n,m,cas=0;
int bl[N],Bcnt,idx,dfn[N],low[N];
int fcut,cut,book[N];
ll ans1,ans2;
template <class t>void rd(t &x){
    x=0;int w=0;char ch=0;
    while(!isdigit(ch)) w|=ch=='-',ch=getchar();
    while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    x=w?-x:x;
}

int head[N],tot=0,root,kid;
struct edge{int u,v,nxt;}e[N<<1];
void add(int u,int v){
    e[++tot]=(edge){u,v,head[u]};head[u]=tot;
}
void tarjan(int u,int fa){
    dfn[u]=low[u]=++idx;
    for(int i=head[u],v;i;i=e[i].nxt){
        v=e[i].v;
        if(!dfn[v]){
            tarjan(v,u),low[u]=min(low[u],low[v]);
            if(dfn[u]<=low[v]){
            if(u!=root) book[u]=1;
            else ++kid;
            }
        }
        else if(v!=fa&&low[u]>dfn[v]) low[u]=dfn[v];
    }
}

void dfs(int u){
    bl[u]=Bcnt,++fcut;
    for(int i=head[u],v;i;i=e[i].nxt){
        v=e[i].v;
        if(book[v]&&bl[v]!=Bcnt) ++cut,bl[v]=Bcnt;
        if(!bl[v]) dfs(v);
    }
}

void clean(){
    tot=Bcnt=Bcnt=idx=n=0;ans1=0ll,ans2=1ll;
    for(int i=0;i<=m+1;++i) head[i]=dfn[i]=bl[i]=book[i]=low[i]=0;
}

int main(){
    freopen("in.txt","r",stdin);
    while(scanf("%d",&m)==1&&m){
        clean();
        for(int i=1,u,v;i<=m;++i) rd(u),rd(v),add(u,v),add(v,u),n=max(n,max(u,v));
        for(int i=1;i<=n;++i)
        if(!dfn[i]){
            kid=0,root=i,tarjan(i,i);
            if(kid>1) book[i]=1;
        }
        for(int i=1;i<=n;++i)
        if(!bl[i]&&!book[i]){
            ++Bcnt,fcut=cut=0;
            dfs(i);
            if(!cut){
                if(fcut==1) ans1+=1ll;
                else ans1+=(ll)2,ans2*=(ll)(fcut-1)*fcut/2;
            }
            if(cut==1) ans1+=1ll,ans2*=(ll)fcut;
        }
        printf("Case %d: ",++cas);
        printf("%lld %lld\n",ans1,ans2);
    } 
    return 0;
}

 

上一篇:HDU 6610 Game


下一篇:关于字典中 创建, 增 改 查 差以及嵌套,排序, 遍历的浅识