Tarjan水题系列(2):HNOI2012 矿场搭建

题目:

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

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

大意:

求 在一个无向图中放若干特殊点 使删除任意一个节点后剩下的每一个连通块中都有特殊点的最小特殊点个数与方案数

思路:

若删除的点不是割点 则对整张图无影响

若是割点 则图被分为若干连通块 每个连通块中都必须有特殊点

由于只有删除割点时才会影响图的连通块个数所以这里将图简化 变为一个个点复连通分量通过共有割点相连

很容易发现 若一个点复连通分量内只有一个割点 那么该割点删除后该分量将独立出去 故必须有一个特殊点

因为只有一个割点的点复连通分量至少有2个 即原连通图至少有2个特殊点 删除一个割点 原图最终至少剩下一个特殊点 所以有两个及以上割点的点复连通分量不会受到影响

0个割点时 要保留两个特殊点 删掉一个特殊点怎么玩?

至此算法已经显然易见了

下面是代码:

 

 1 #include <cstdio>
 2 #include <iostream>
 3 #include <memory.h>
 4 #define MAXX 600
 5 #define MAX(a,b) (a>b?a:b)
 6 #define MIN(a,b) (a<b?a:b)
 7 #define r(x) x=read()
 8 using namespace std;
 9 typedef long long ll;
10 int fi,flag[MAXX],dfn[MAXX],low[MAXX],k,n,u,to,
11     cnt,sta[MAXX],ans2=0,top,num,id[MAXX][MAXX],T[MAXX],h[MAXX],t;
12 ll ans=1;
13 int read()
14 {
15   char ch=0;int w=0;
16   while(ch<'0'||ch>'9'){ch=getchar();}
17   while(ch>='0'&&ch<='9'){w=w*10+ch-'0';ch=getchar();}
18   return w;
19 }
20 struct edge{int to,nex;}e[MAXX<<3];
21 void add(int u,int to)
22 {
23   cnt++;
24   e[cnt]=(edge){to,h[u]};
25   h[u]=cnt;
26 }
27 void tarjan(int now)
28 {
29   int tot=0,sign=0;
30   dfn[now]=low[now]=++k;
31   sta[++top]=now;
32   for(int i=h[now];i;i=e[i].nex)
33   {
34     if(!dfn[e[i].to]) 
35     {
36       tot++,tarjan(e[i].to),low[now]=MIN(low[now],low[e[i].to]);
37       if((fi==now&&tot>1)||(low[e[i].to]>=dfn[now]&&fi!=now))
38           flag[now]=1;
39       if(dfn[now]<=low[e[i].to])
40       {
41           num++;
42           while(sta[top]!=now)
43           {
44             id[num][++T[num]]=sta[top];
45             top--;
46         }
47         id[num][++T[num]]=now;
48       }
49     }
50     else
51         low[now]=MIN(low[now],dfn[e[i].to]);
52   }
53 }
54 void solve()
55 {
56   memset(h,0,sizeof(h));
57   memset(dfn,0,sizeof(dfn));
58   memset(T,0,sizeof(T));
59   memset(flag,0,sizeof(flag));
60   memset(low,0,sizeof(low));
61   ans=1,top=0,k=0,ans2=0,cnt=0,num=0;
62   r(n);
63   if(n==0) exit(0);
64   for(int i=1;i<=n;++i)
65     r(u),r(to),add(u,to),add(to,u);
66   for(int i=1;i<=500;++i)
67     if(h[i]&&!dfn[i])
68       fi=i,tarjan(i); 
69   for(int i=1;i<=num;++i)
70   {
71       int len=T[i],z=0;
72     for(int j=1;j<=len;++j)
73     {
74       if(flag[id[i][j]]) z++;
75     }
76     if(z==0) ans2+=2,ans=ans*len*(len-1)/2;
77     else if(z==1) ans2++,ans=ans*(len-1);
78   }
79   printf("Case %d: %d %lld\n",t,ans2,ans);
80 }
81 int main()
82 {
83   while(1){t++;solve();}
84   return 0;
85 }

 

posted @ 2019-05-19 21:01  Dah  阅读(137)  评论(0编辑  收藏  举报