BZOJ2430&&洛谷P3225[HNOI2012]矿场搭建

版权声明:我这么弱的蒟蒻,虽然博文不是很好,但也请标明转发地址喵! https://blog.csdn.net/ACerAndAKer/article/details/82839399

无向图tarjan求割点

很显然,我们求出所有联通分量后,若这个联通分量内有

1个割点,那么只能在这个点上建出口,因为这个点坏了其他点就出不去了

2+个割点,那么这个联通分量是安全的,因为其中一个坏了还能走另一个

0个割点,那么这个联通分量至少需要两个出口,否则也无法出去

然后方案数就根据上面这些乱搞一下就好了

代码

//By AcerMo
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define lli long long int
using namespace std;
const int M=200500;
int n,m,ans,emm;
int to[M],nxt[M],head[M],cnt;
int dfn[M],low[M],cut[M],fa[M],ind,ti;
inline int read()
{
	int x=0;char ch=getchar();
	while (ch>'9'||ch<'0') ch=getchar();
	while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return x;
}
inline void add(int x,int y)
{
	to[++cnt]=y;nxt[cnt]=head[x];head[x]=cnt;
	to[++cnt]=x;nxt[cnt]=head[y];head[y]=cnt;
	return ;
}
inline void tarjan(int x,int f)
{
	low[x]=dfn[x]=++ind;int son=0;
	for (int i=head[x];i;i=nxt[i])
	if (!dfn[to[i]]) 
	{
		tarjan(to[i],x);son++;
		low[x]=min(low[x],low[to[i]]);
		if (low[to[i]]>=dfn[x]) cut[x]=1;
	}
	else if (f!=to[i]) low[x]=min(low[x],dfn[to[i]]);
	if (son==1&&!f) cut[x]=0;
	return ;
}
inline void dfs(int x)
{
	fa[x]=ti;
	if (cut[x]) return ;emm++;
	for (int i=head[x];i;i=nxt[i])
	{
		if (fa[to[i]]!=ti&&cut[to[i]]) ans++,fa[to[i]]=ti;
		if (!fa[to[i]]&&!cut[to[i]]) dfs(to[i]);
	}
	return ;
}
inline void clean()
{
	fill(low,low+n+1,0);
	fill(dfn,dfn+n+1,0);fill(cut,cut+n+1,0);
	fill(head,head+n+1,0);fill(fa,fa+n+1,0);
	return (void)(ti=ind=n=cnt=0);
}
signed main()
{
	int cas=0;
	while ((m=read())&&m)
	{
		int x,y;clean();
		lli qlm=0,que=1;
		for (int i=1;i<=m;i++)
		{
			x=read(),y=read();
			add(x,y),n=max(n,max(x,y));
		}
		for (int i=1;i<=n;i++)
		if (!dfn[i]) tarjan(i,0);
		for (int i=1;i<=n;i++)
		if (!fa[i]&&!cut[i])
		{
			ti++;ans=emm=0;dfs(i);
			if (!ans) qlm+=2,que*=(emm*(emm-1)/2);
			if (ans==1) qlm++,que*=emm;
		}
		printf("Case %d: %lld %lld\n",++cas,qlm,que);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/ACerAndAKer/article/details/82839399