P3225 [HNOI2012]矿场搭建 题解

题目大意

给定一个由隧道连接挖煤点的无向图,保证在任何一个工地发生坍塌后,其他的挖煤点的工人都有一条道路通向救援出口

求最少的救援出口处和总方案数

P3225 [HNOI2012]矿场搭建

问题求解

显然

如果没有割点

至少需要建立两个出口

从任意非割点的地方选择两个点建立

如果这个分组只有一个割点

只需要在分组内设立一个出口

可以设立在任意一个非割点的地方

如果有两个及以上个割点,则无需建立,可以直接到达其他联通块

代码实现

#include<cstdio>
#include<vector>
#include<stack>
#include<cstring>
using namespace std;
typedef long long LL;
const int maxn=505;
int T,N,M,K,cnt,Ans1,c[maxn],low[maxn],dfn[maxn],root;
LL Ans2;
vector<int> e[maxn],DCC[maxn];
stack<int> s;
inline int read(){
	int ret=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
	while(ch<='9'&&ch>='0')ret=ret*10+ch-'0',ch=getchar();
	return ret*f;
}
void Tarjan(int x){
	int fa=0; s.push(x);
	low[x]=dfn[x]=++cnt;
	for(int j=0;j<e[x].size();j++){
		int y=e[x][j];
		if(!dfn[y]){
			Tarjan(y);
			low[x]=min(low[x],low[y]);
			if(low[y]>=dfn[x]){
				fa++;K++;
				if(x!=root||fa>1)c[x]=1;
				DCC[K].push_back(x);
				int z=0;
				do{
					DCC[K].push_back(z=s.top());s.pop();
				}while(z!=y);
			}
		}
		else low[x]=min(low[x],dfn[y]);
	}
}
int main(){
	freopen("P3225.in","r",stdin);
	freopen("P3225.out","w",stdout);
	while(true){
		N=read();if(!N)break;
		while(!s.empty()) s.pop();
		for(int i=1;i<=N;i++) e[i].clear();
		for(int i=1;i<=K;i++) DCC[i].clear();
		M=K=Ans1=cnt=0;Ans2=1;
		memset(low,0,sizeof low);
		memset(c,0,sizeof c);
		memset(dfn,0,sizeof dfn);
		for(int i=1;i<=N;i++){
			int x=read(),y=read();
			e[x].push_back(y);e[y].push_back(x);
			M=max(M,x);M=max(M,y);
		}
		for(int i=1;i<=M;i++)if(!dfn[i]){
			root=i;Tarjan(i);
		}
		for(int i=1;i<=K;i++){
			int num=0,cnt=DCC[i].size();
			for(int j=0;j<cnt;j++)num+=c[DCC[i][j]];
//			for(int j=0;j<cnt;j++)printf("%d ",DCC[i][j]);
			printf("
");
			if(num==1)Ans1++,Ans2*=cnt-1;
			if(num==0)Ans1+=2,Ans2*=cnt*(cnt-1)>>1;
		}
//		printf("%d %lld
",Ans1,Ans2);
		printf("Case %d: %d %lld
",++T,Ans1,Ans2);
	}
	return 0;
}

关于其他

貌似有三倍经验???

原文地址:https://www.cnblogs.com/martian148/p/15177346.html