bzoj2730: [HNOI2012]矿场搭建

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2730

思路:点双缩点

对于一个点双,如果它不与任何一个割点相连,那它就要建两个出口

与一个相连,就见一个

与两个以上相连就不用建

方案就是每个点双去掉割点的点数之积

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int maxn=1010,maxm=maxn<<1;
typedef long long ll;
using namespace std;
int n,m,now[maxn],son[maxm],pre[maxm],T;
int low[maxn],dfn[maxn],tim,tot,cas,deg,cnt,num,vis[maxn],ans1,root;bool cut[maxn];
ll ans2;
void clear(){
	memset(now,0,sizeof(now)),memset(low,0,sizeof(low)),memset(dfn,0,sizeof(dfn));
	memset(cut,0,sizeof(cut)),memset(vis,0,sizeof(vis)),tot=n=tim=T=ans1=0,cas++,ans2=1;
}
void add(int a,int b){pre[++tot]=now[a],now[a]=tot,son[tot]=b;}

void tarjan(int x,int fa){
	dfn[x]=low[x]=++tim;//printf("%d\n",x);
	for (int y=now[x];y;y=pre[y]){
		if (son[y]==fa) continue;
		if (!dfn[son[y]]) tarjan(son[y],x),low[x]=min(low[x],low[son[y]]);
		else{low[x]=min(low[x],dfn[son[y]]);continue;}
		//printf("fuckpp%d %d\n",x,son[y]);
		if (dfn[x]<=low[son[y]]){if (x==root) deg++;else cut[x]=1;}
	}
}

void dfs(int x){
	//printf("%d\n",x);
	vis[x]=T;if (cut[x]) return; cnt++;
	for (int y=now[x];y;y=pre[y]){
		if (cut[son[y]]&&vis[son[y]]!=T) num++,vis[son[y]]=T;
		if (!vis[son[y]]) dfs(son[y]);
	}
}

int main(){
	scanf("%d",&m);
	while (m){
		clear();
		for (int i=1,x,y;i<=m;i++) scanf("%d%d",&x,&y),add(x,y),add(y,x),n=max(x,n),n=max(n,y);
		for (int i=1;i<=n;i++){
			if (!dfn[i]){tarjan(root=i,0);if (deg>=2) cut[root]=1;}//root要特判,因为要有两个才算割点
			deg=0;
		}
		//for (int i=1;i<=n;i++) {printf("%d %d %d\n",i,dfn[i],low[i]);if (cut[i]) printf("%d\n",i);}
		for (int i=1;i<=n;i++)
			if (!vis[i]&&!cut[i]){
				++T,cnt=num=0,dfs(i);//T时间戳,cnt:块的大小,num:块中割点数
				if (!num) ans1+=2,ans2*=cnt*(cnt-1)/2;
				if (num==1) ans1++,ans2*=cnt;
			}
		printf("Case %d: %d %lld\n",cas,ans1,ans2);
		scanf("%d",&m);
	}
	return 0;
}


posted @ 2016-01-03 20:56  orzpps  阅读(139)  评论(0编辑  收藏  举报