[题解]bzoj2730(HNOI2012)矿场搭建

来源:互联网 发布:炉石百宝箱 mac 编辑:程序博客网 时间:2024/04/30 07:34

Description

煤矿工地可以看成是由隧道连接挖煤点组成的无向图。为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处。于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口。请写一个程序,用来计算至少需要设置几个救援出口,以及不同最少救援出口的设置方案总数。

Input

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

Output

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

Sample Input

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

Sample Output

Case 1: 2 4
Case 2: 4 1

HINT

Case 1 的四组解分别是(2,4),(3,4),(4,5),(4,6)

Case 2 的一组解为(4,5,6,7)

Solution

        很容易看出这一题和点双有关。如果一个点双联通分量中没有割点,那么必须要建两个出口,塌了一个走另一个,方案数为C(size,2)。如果一个点双中有且只有一个割点,那么必须在点双中除了割点之外的地方建一个出口,如果割点塌了就走出口,如果出口塌了就从割点那边走到别的点双去,方案数为size-1。如果一个点双中有大于等于2个点双,那么没必要建出口,因为不管你塌哪里都可以走到别的点双去。所以只需要Tarjan处理点双就可以了。

代码:

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;inline int read(){int xx=0,f=1;char ch=getchar();for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())xx=xx*10+ch-'0';return xx*f;}typedef long long LL;const int maxn=1010;struct edge{int to,next,size,same;}e[maxn];int n,m,ans,num,T,cnt,head[maxn],dfn[maxn],low[maxn];int sta[maxn],top,belong[maxn];LL sum,C[maxn][3];bool is_cut[maxn],exist[maxn],vis[maxn];void adde(int u,int v){e[++num].to=v;e[num].next=head[u];head[u]=num;}void add(int u,int v){adde(u,v);e[num].same=num+1;adde(v,u);e[num].same=num-1;}void dfs(int x){dfn[x]=low[x]=++cnt;sta[++top]=x;for(int i=head[x];i;i=e[i].next){if(!dfn[e[i].to]){dfs(e[i].to);low[x]=min(low[x],low[e[i].to]);if(low[e[i].to]>=dfn[x]){belong[x]++;}}else{low[x]=min(low[x],dfn[e[i].to]);}}}void Tarjan(int x){dfn[x]=low[x]=++cnt;sta[++top]=x;for(int i=head[x];i;i=e[i].next){if(!dfn[e[i].to]){Tarjan(e[i].to);low[x]=min(low[x],low[e[i].to]);if(low[e[i].to]>=dfn[x]){int size=0,cnum=0;do{if(belong[sta[top]]>=2){cnum++;}size++;}while(sta[top--]!=e[i].to);if(belong[x]>=2){cnum++;}size++;if(!cnum){ans+=2;sum*=C[size][2];}else if(cnum==1){ans++;sum*=(size-1);}}}else{low[x]=min(low[x],dfn[e[i].to]);}}}int main(){for(int i=0;i<=maxn-10;i++){C[i][0]=1;}for(int i=1;i<=maxn-10;i++){for(int j=1;j<=i&&j<=2;j++){C[i][j]=C[i-1][j]+C[i-1][j-1];}}m=read();while(m){memset(dfn,0,sizeof dfn);cnt=0;memset(sta,0,sizeof sta);top=0;memset(exist,0,sizeof exist);memset(head,0,sizeof head);num=0;memset(belong,0,sizeof belong);ans=0;sum=1;for(int i=1,u,v;i<=m;i++){u=read();v=read();n=max(n,max(u,v));exist[u]=exist[v]=true;add(u,v);}for(int i=1;i<=n;i++){if(exist[i]&&!dfn[i]){dfs(i);}else{belong[i]++;}}memset(dfn,0,sizeof dfn);cnt=0;memset(sta,0,sizeof sta);top=0;for(int i=1;i<=n;i++){if(exist[i]&&!dfn[i]){Tarjan(i);}}printf("Case %d: %d %lld\n",++T,ans,sum);m=read();}return 0;}


0 0
原创粉丝点击