1. 程式人生 > 其它 >P3225 [HNOI2012]礦場搭建 題解

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("\n");
			if(num==1)Ans1++,Ans2*=cnt-1;
			if(num==0)Ans1+=2,Ans2*=cnt*(cnt-1)>>1;
		}
//		printf("%d %lld\n",Ans1,Ans2);
		printf("Case %d: %d %lld\n",++T,Ans1,Ans2);
	}
	return 0;
}

關於其他

貌似有三倍經驗???