【HNOI 2012】矿场搭建

【题目】

传送门

题目描述:

煤矿工地可以看成是由隧道连接挖煤点组成的无向图。为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处。于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口。

请写一个程序,用来计算至少需要设置几个救援出口,以及不同最少救援出口的设置方案总数。

输入格式:

输入文件有若干组数据,每组数据的第一行是一个正整数 n n n n 500 500 ),表示工地的隧道数,接下来的 n n 行每行是用空格隔开的两个整数 s s t t ,表示挖煤点 s s 与挖煤点 t t 由隧道直接连接。输入数据以 0 0 结尾。

输出格式:

输入文件中有多少组数据,输出文件中就有多少行。每行对应一组输入数据的结果:

其中第 i 行以 Case i: 开始(注意大小写,Casei 之间有空格,i: 之间无空格,: 之后有空格),其后是用空格隔开的两个正整数,第一个正整数表示对于第 i 组输入数据至少需要设置几个救援出口,第二个正整数表示对于第 i 组输入数据不同最少救援出口的设置方案总数。

输入数据保证答案小于 2 64 2^{64} 。输出格式参照以下输入输出样例。

样例数据:

输入
9
1 3
4 1
3 5
1 2
2 6
1 5
6 3
1 6
3 2
6
1 2
1 3
2 4
2 5
3 6
3 7
0

输出
Case 1: 2 4
Case 2: 4 1

备注:

【样例说明】
Case 1 的四组解分别是 (2,4),(3,4),(4,5),(4,6)
Case 2 的一组解为 (4,5,6,7)


【分析】

题解:点双连通分量

应该很容易可以知道这是一道点双的题吧

如果一张图中没有割点,那么就可以随便选两个点设置出口(不能只选一个,因为如果选的那个被毁了,就 g g gg 了),然后若总共有 t o t tot 个点,方案数就是 t o t ( t o t 1 ) 2 \frac{tot*(tot-1)}{2}

不然的话,我们就把所有的割点删了,在剩下的连通块里设置出口,出口数就是连通块个数,方案就是根据乘法原理把点数乘起来就行了

还有多组数据一定要注意初始化!!!


【代码】

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 1005
using namespace std;
int t,tot,cnt,num,time,root;
int first[N],v[N],nxt[N];
int dfn[N],low[N],vis[N];
bool cut[N];
void init()
{
	num=0;
	memset(dfn,0,sizeof(dfn));
	memset(low,0,sizeof(low));
	memset(first,0,sizeof(first));
	memset(cut,false,sizeof(cut));
	memset(vis,false,sizeof(vis));
}
void add(int x,int y)
{
	t++;
	nxt[t]=first[x];
	first[x]=t;
	v[t]=y;
}
void Tarjan(int x)
{
	int i,j,child=0;
	dfn[x]=low[x]=++time;
	for(i=first[x];i;i=nxt[i])
	{
		j=v[i];
		if(!dfn[j])
		{
			child++;
			Tarjan(j);
			low[x]=min(low[x],low[j]);
			if(x==root&&child>1)  cut[x]=true;
			if(x!=root&&dfn[x]<=low[j])  cut[x]=true;
		}
		else  low[x]=min(low[x],dfn[j]);
	}
}
void dfs(int x)
{
	int i,j;
	vis[x]=num;
	if(cut[x])  return;
	tot++;
	for(i=first[x];i;i=nxt[i])
	{
		j=v[i];
		if(cut[j]&&vis[j]!=num)  cnt++,vis[j]=num;
		if(!vis[j])  dfs(j);
	}
}
int main()
{
	int n,i,x,y,cases=0;
	while(~scanf("%d",&n))
	{
		init();
		int point=0;
		if(!n)  break;
		for(i=1;i<=n;++i)
		{
			scanf("%d%d",&x,&y);
			add(x,y),add(y,x);
			point=max(point,max(x,y));
		}
		for(i=1;i<=point;++i)
		  if(!dfn[i])
		    Tarjan(root=i);
		int ans1=0;
		long long ans2=1;
		for(i=1;i<=point;++i)
		{
			if(!cut[i]&&!vis[i])
			{
				num++,tot=cnt=0,dfs(i);
				if(cnt==1)  ans1++,ans2*=tot;
				if(!cnt)  ans1+=2,ans2*=tot*(tot-1)/2;
			}
		}
		printf("Case %d: %d %lld\n",++cases,ans1,ans2);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/forever_dreams/article/details/83420668
今日推荐