点-双联通分量。
显然割顶不放,一个双联通分量若有多个割顶,也不用放,剩下的就是乘法原理了。
特判:原图是双联通的,任意找两个点放就好了。
WF2011原题。
#include<iostream> #include<cstdio> #include<cstring> #include<stack> #include<vector> using namespace std; const int N=500+5; typedef long long ll; struct Edge{int to,next;}e[N*2]; struct edge{int u,v;}; int head[N],cnt,bcc_cnt,bccno[N],pre[N],dfs_clock; stack<edge>s; vector<int>bcc[N]; bool iscut[N]; void ins(int u,int v){ cnt++;e[cnt].to=v;e[cnt].next=head[u];head[u]=cnt; } int dfs(int u,int fa){ int lowu=pre[u]=++dfs_clock; int child=0; for(int i=head[u];i;i=e[i].next){ int v=e[i].to; if(!pre[v]){ s.push((edge){u,v}); child++; int lowv=dfs(v,u); lowu=min(lowu,lowv); if(lowv>=pre[u]){ iscut[u]=true; bcc_cnt++;bcc[bcc_cnt].clear(); while(true){ edge tmp=s.top();s.pop(); if(bccno[tmp.u]!=bcc_cnt)bccno[tmp.u]=bcc_cnt,bcc[bcc_cnt].push_back(tmp.u); if(bccno[tmp.v]!=bcc_cnt)bccno[tmp.v]=bcc_cnt,bcc[bcc_cnt].push_back(tmp.v); if(tmp.u==u&&tmp.v==v)break; } } } else if(pre[v]<pre[u]&&v!=fa){ s.push((edge){u,v}); lowu=min(lowu,pre[v]); } } if(fa<0&&child==1)iscut[u]=false; return lowu; } void find_bcc(int n){ memset(pre,0,sizeof(pre)); memset(iscut,false,sizeof(iscut)); memset(bccno,0,sizeof(bccno)); dfs_clock=bcc_cnt=0; for(int i=1;i<=n;i++) if(!pre[i])dfs(i,-1); } int main(){ int n,kase=0; while(scanf("%d",&n)&&n){ int u,v,node=0; memset(head,0,sizeof(head));cnt=0; for(int i=1;i<=n;i++){ scanf("%d%d",&u,&v); ins(u,v);ins(v,u); node=max(node,u);node=max(node,v); } find_bcc(node); ll ans1=0,ans2=1; if(bcc_cnt==1){ ans1=2;ans2=bcc[1].size()*(bcc[1].size()-1)/2; }else{ for(int i=1;i<=bcc_cnt;i++){ int cut_cnt=0; for(int j=0;j<bcc[i].size();j++) if(iscut[bcc[i][j]])cut_cnt++; if(cut_cnt==1){ ans1++; ans2*=(ll)(bcc[i].size()-cut_cnt); } } } printf("Case %d: %lld %lld\n",++kase,ans1,ans2); } return 0; }