BZOJ2730:[HNOI2012]矿场搭建——题解
https://www.lydsy.com/JudgeOnline/problem.php?id=2730
https://www.luogu.org/problemnew/show/P3225
听说这是一道水题我就来做了,然而并不水……
煤矿工地可以看成是由隧道连接挖煤点组成的无向图。为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处。于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口。
请写一个程序,用来计算至少需要设置几个救援出口,以及不同最少救援出口的设置方案总数。
把点双缩起来,然后对于每个点双:
1.没有割点
说明点双内的点无法出去到其他点双,在这个点双内就需要两个出口,防止其中一个被炸。
(这是真的坑,忘了还可以炸出口orz)
2.有一个割点
只有一条路通往外面的点双,所以在点双内建一个出口。
3.有多个割点
有多条路通往外面的点双,炸了一条仍然与外面联通,所以不需要出口。
之后乘法原理做即可。
#include<cstdio> #include<cmath> #include<vector> #include<iostream> #include<stack> #include<cstring> #include<algorithm> #include<cctype> using namespace std; typedef long long ll; const int N=1e3+5; inline int read(){ int x=0,w=1;char ch=0; while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} return x*w; } struct node{ int u,v,nxt; }e[N]; int head[N],cnt,n,m,rtson; int dfn[N],low[N],to[N],t,l; bool cut[N]; stack<int>q; vector<int>p[N]; inline int add(int u,int v){ e[++cnt].v=v;e[cnt].u=u;e[cnt].nxt=head[u];head[u]=cnt; } void tarjan(int u,int f){ dfn[u]=low[u]=++t; for(int i=head[u];i;i=e[i].nxt){ int v=e[i].v; if(!dfn[v]){ q.push(i); tarjan(v,u); low[u]=min(low[u],low[v]); if(low[v]>=dfn[u]){ int num;cut[u]=1; p[++l].clear(); do{ num=q.top();q.pop(); int uu=e[num].u,vv=e[num].v; if(to[uu]!=l){ to[uu]=l; p[l].push_back(uu); } if(to[vv]!=l){ to[vv]=l; p[l].push_back(vv); } }while(num!=i); } if(!f)rtson++; }else if(low[u]>dfn[v]&&f!=v){ q.push(i); low[u]=dfn[v]; } } if(!f&&rtson<2)cut[u]=0; } inline void init(){ cnt=t=l=rtson=n=0; memset(head,0,sizeof(head)); memset(dfn,0,sizeof(dfn)); memset(cut,0,sizeof(cut)); memset(to,0,sizeof(to)); } int main(){ int test=0; while(scanf("%d",&m)&&m){ init();test++; for(int i=1;i<=m;i++){ int u=read(),v=read(); add(u,v);add(v,u); n=max(n,u);n=max(n,v); } for(int i=1;i<=n;i++) if(!dfn[i])tarjan(i,0); ll num=0,ans=1; for(int i=1;i<=l;i++){ int sum=0; for(int j=0;j<p[i].size();j++){ int u=p[i][j]; if(cut[u])sum++; if(sum>1)break; } ll sz=p[i].size(); if(!sum){ int tmp=min(2LL,sz); num+=tmp; if(tmp==2)ans*=(ll)sz*(sz-1)/2; }else if(sum==1){ num++; ans*=(sz-1); } } printf("Case %d: %lld %lld\n",test,num,ans); } return 0; }
+++++++++++++++++++++++++++++++++++++++++++
+本文作者:luyouqi233。 +
+欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/ +
+++++++++++++++++++++++++++++++++++++++++++