luogu P3225 [HNOI2012]矿场搭建 |Tarjan割点

题目描述

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

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

输入格式

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

输出格式

输入文件中有多少组数据,输出文件 output.txt 中就有多少行。每行对应一组输入数据的 结果。其中第 i 行以 Case i: 开始(注意大小写,Case 与 i 之间有空格,i 与:之间无空格,: 之后有空格),其后是用空格隔开的两个正整数,第一个正整数表示对于第 i 组输入数据至少需 要设置几个救援出口,第二个正整数表示对于第 i 组输入数据不同最少救援出口的设置方案总 数。输入数据保证答案小于 2^64。输出格式参照以下输入输出样例。


两个割点不用建

一个割点建一个

没有割点,建两个

#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1005,M=N*3;
#define int long long
inline int read(){
    int x=0,f=1; char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-48;ch=getchar();}
    return x*f;
}
int nxt[M],head[N],go[M],tot;
inline void add(int u,int v){
	nxt[++tot]=head[u],head[u]=tot,go[tot]=v;
}
int dfn[N],low[N],st[N],cut[N],top,num,col,root;
vector<int>dcc[N];
void Tarjan(int u,int fa){
	dfn[u]=low[u]=++num;
	st[++top]=u;
	if(u==root&&head[u]==0){
		dcc[++col].push_back(u);
		return;
	}
	int flag=0;
	for(int i=head[u];i;i=nxt[i]){
		int v=go[i];
		if(!dfn[v]){
			Tarjan(v,u);
			low[u]=min(low[u],low[v]);
			if(low[v]>=dfn[u]){
				flag++;
				if(u!=root||flag>1)cut[u]=1;
				col++;
				int z;
				do{
					z=st[top--];
					dcc[col].push_back(z);
				}while(z!=v);
				dcc[col].push_back(u);
			}
		}else low[u]=min(low[u],dfn[v]);
	}
}
int n;
signed main(){
	int Case=0;
	while(scanf("%lld",&n),n){
		int rt=0,res1=0,res2=1;
		tot=0,num=0,col=0;
		memset(cut,0,sizeof(cut));
		memset(nxt,0,sizeof(nxt));
		memset(dfn,0,sizeof(dfn));
		memset(low,0,sizeof(low));
		memset(head,0,sizeof(head));
		for(int i=1,u,v;i<=n;i++){
			u=read(),v=read();
			rt=max(max(u,v),rt);
			add(u,v),add(v,u);
		}
		for(int i=1;i<=rt;i++)
		if(!dfn[i]){ root=i; Tarjan(i,i); }
		for(int i=1;i<=col;i++){
			int num=0;
			for(int j=0;j<dcc[i].size();j++)
			if(cut[dcc[i][j]])num++;
			if(num==1){
				res1++;
				res2=res2*(dcc[i].size()-1);
			}
			if(num==0){
				if(dcc[i].size()>1){
					res1+=2;
					res2=res2*(dcc[i].size()*(dcc[i].size()-1)/2);
				}else res1++;
			}
			dcc[i].clear();
		}
		printf("Case %lld: %lld %lld\n",++Case,res1,res2);
	}
	return 0;

}

猜你喜欢

转载自www.cnblogs.com/naruto-mzx/p/12712733.html
今日推荐