[HNOI2012]矿场搭建(割点)
题目描述
煤矿工地可以看成是由隧道连接挖煤点组成的无向图。为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处。于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口。
请写一个程序,用来计算至少需要设置几个救援出口,以及不同最少救援出口的设置方案总数。
输入格式
输入文件有若干组数据,每组数据的第一行是一个正整数 N(N<=500),表示工地的隧道数,接下来的 N 行每行是用空格隔开的两个整数 S 和 T,表示挖 S 与挖煤点 T 由隧道直接连接。输入数据以 0 结尾。
输出格式
输入文件中有多少组数据,输出文件 output.txt 中就有多少行。每行对应一组输入数据的 结果。其中第 i 行以 Case i: 开始(注意大小写,Case 与 i 之间有空格,i 与:之间无空格,: 之后有空格),其后是用空格隔开的两个正整数,第一个正整数表示对于第 i 组输入数据至少需 要设置几个救援出口,第二个正整数表示对于第 i 组输入数据不同最少救援出口的设置方案总 数。输入数据保证答案小于 2^64。输出格式参照以下输入输出样例。
#include<bits/stdc++.h> #define re return #define ll long long #define inc(i,l,r) for(int i=l;i<=r;++i) using namespace std; template<typename T>inline void rd(T&x) { char c;bool f=0; while((c=getchar())<'0'||c>'9')if(c=='-')f=1; x=c^48; while((c=getchar())>='0'&&c<='9')x=x*10+(c^48); if(f)x=-x; } const int maxn=5005,maxm=10005; ll n,m,k=1,rs,GD,tot,col,hd[maxn]; ll low[maxn],dfn[maxn],isgd[maxn],vis[maxn]; struct node{ int to,nt; }e[maxm<<1]; ll ans1,ans=1; inline void add(int x,int y) { e[++k].to=y;e[k].nt=hd[x];hd[x]=k; e[++k].to=x;e[k].nt=hd[y];hd[y]=k; } stack<int>s; inline void tarjan(int x,int fa) { dfn[x]=low[x]=++tot; for(int i=hd[x];i;i=e[i].nt) { int v=e[i].to; if(v==fa)continue; if(!dfn[v]) { tarjan(v,x); low[x]=min(low[x],low[v]); if(!fa)++rs; else if(dfn[x]<=low[v])//割点 isgd[x]=1; } else low[x]=min(dfn[v],low[x]); } } inline void dfs(int x) { ++rs; vis[x]=col; for(int i=hd[x];i;i=e[i].nt) { int v=e[i].to; //如果是割点 且没算在此环内,GD+1 if(isgd[v]&&vis[v]!=col)++GD,vis[v]=col; //没有被访问过 else if(!vis[v]) dfs(v); } } int main() { freopen("in.txt","r",stdin); int x,y,z,vv=0,maxx; rd(n); while(n) { k=1; maxx=col=tot=0; vv++; memset(vis,0,sizeof vis); memset(hd,0,sizeof hd); memset(dfn,0,sizeof dfn); memset(low,0,sizeof low); memset(isgd,0,sizeof isgd); inc(i,1,n) { rd(x),rd(y); maxx=max(maxx,max(x,y)); add(x,y); } //trajan找割点 inc(i,1,maxx) if(!dfn[i]) { rs=0; tarjan(i,0); if(rs>1)isgd[i]=1; } //Get_ans ans=1;ans1=0; inc(i,1,maxx) if(!vis[i]&&(!isgd[i])) { ++col; rs=GD=0; dfs(i); //如果不与割点相连 //环内任选两点以防止选一个点被毁 if(GD==0) { ans*=(rs-1)*rs/2; ans1+=2; } //如果只与一个割点相连 //环内需要一个点防止割点被毁 else if(GD==1) { ++ans1; ans*=rs; } //如果有两个及以上的割点 //任跑一个割点都可以走 } printf("Case %d: %lld %lld\n",vv,ans1,ans); rd(n); } re 0; }