Luogu P3225 [HNOI2012]矿场搭建

题目
一看题目首先求个割点肯定是没问题的。
然后考虑一下各个v-dcc。
如果一个连通块没有割点,那么它就是孤立的,我们至少要放两个,这样在塌了一个的情况下和还能走另一个。当然如果只有一个点那么久只放一个。
如果v-dcc有一个割点,那么我们可以把它理解为一个叶子节点。
如果这个割点塌了那么这个叶子节点就出不去了,所以我们需要在这个v-dcc的非割点的节点中放一个,方案数为除了割点以外的这个v-dcc的大小。
如果一个v-dcc有两个或以上的割点,那么说明它与两个或两个以上的v-dcc相邻,并且在塌了一个点的情况下一定能够到达一个有一个割点的放了的v-dcc,所以不需要放。

#include<bits/stdc++.h>
#define N 507
using namespace std;
namespace IO
{
    char ibuf[(1<<21)+1],*iS,*iT;
    char Get() { return (iS==iT? (iT=(iS=ibuf)+fread(ibuf,1,(1<<21)+1,stdin),(iS==iT? EOF:*iS++)):*iS++); }
    int read(){int x=0;char ch=Get();while(ch>57||ch<48)ch=Get();while(ch>=48&&ch<=57)x=x*10+(ch^48),ch=Get();return x;}
}
using namespace IO;
vector<int>G[N];
int low[N],dfn[N],Time,flg[N],root,sum,num,cut,cnt,vis[N];
void tarjan(int u,int fa)
{
    dfn[u]=low[u]=++Time;
    int i,v;
    for(i=0;i<G[u].size();++i)
	if(!dfn[v=G[u][i]])
	{
	    tarjan(v,u),low[u]=min(low[u],low[v]);
	    if(low[v]>=dfn[u]) if(u^root) flg[u]=1; else ++sum;
	}
	else if(v^fa) low[u]=min(low[u],dfn[v]);
}
void dfs(int u)
{
    int i,v;
    vis[u]=cnt,++num;
    for(i=0;i<G[u].size();++i)
    {
	if(flg[v=G[u][i]]&&vis[v]^cnt) ++cut,vis[v]=cnt;
	if(!vis[v]) dfs(v);
    }
}
int main()
{
    int i,n,m,u,v,T=0,ans;
    long long Ans;
    while(m=read())
    {
	for(i=1;i<N;++i) G[i].clear();
	memset(low,0,sizeof low),memset(dfn,0,sizeof dfn),memset(flg,0,sizeof flg),memset(vis,0,sizeof vis),Time=cnt=n=ans=0,Ans=1;
	for(i=1;i<=m;++i) u=read(),v=read(),G[u].push_back(v),G[v].push_back(u),n=max(n,max(u,v));
	for(i=1;i<=n;++i) if(!dfn[i]){root=i,sum=0,tarjan(i,i);if(sum>=2)flg[i]=1;}
	for(i=1;i<=n;++i)
	{
	    if(!vis[i]&&!flg[i])
	    {
		++cnt,cut=num=0,dfs(i);
		if(!cut) if(num==1) ++ans; else ans+=2,Ans*=1ll*(num-1)*num/2;
		if(cut==1) ++ans,Ans*=num;
	    }
	}
	printf("Case %d: %d %lld
",++T,ans,Ans);
    }
}
原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/11930203.html