容易发现在一个点-双连通分量中最多只要放置一个太平井即可。
进一步分析,如果一个点-双连通分量只有一个衔接点,那么这个点-双连通分量必须要放置一个太平井,因为如果在衔接点处断开,这个分量就会与其他部分隔绝,并且除衔接点外任何点都可以作为太平井。
而如果有两个以上的衔接点那么就不用放置太平井了,因为我们只会断开一个点,这个有两个以上衔接点的双连通分量必然可以由其他只有一个衔接点的双连通分量到达。
当然还有坑爹的特判:原图只有一个双连通分量的话就只要涂任意两个点了。
#include<bits/stdc++.h> using namespace std; const int maxn=100000+10; typedef long long LL; struct Edge { int u,v; Edge(){} Edge(int u,int v):u(u),v(v){} }; int dfs_clock,pre[maxn],low[maxn],bccno[maxn],bcc_cnt; vector<int> G[maxn],bcc[maxn]; bool iscut[maxn]; stack<Edge> S; int DFS(int u,int father) { int child=0; pre[u]=low[u]=++dfs_clock; for(int i=0,size=G[u].size();i<size;i++) { int v=G[u][i]; Edge e=Edge(u,v); if(!pre[v]) { S.push(e); child++; DFS(v,u); low[u]=min(low[u],low[v]); if(pre[u]<=low[v]) { iscut[u]=true; bcc[++bcc_cnt].clear(); while(true) { Edge x=S.top();S.pop(); if(bccno[x.u]!=bcc_cnt) { bcc[bcc_cnt].push_back(x.u); bccno[x.u]=bcc_cnt; } if(bccno[x.v]!=bcc_cnt) { bcc[bcc_cnt].push_back(x.v); bccno[x.v]=bcc_cnt; } if(x.u==u&&x.v==v) break; } } } else if(pre[u]>pre[v]&&v!=father) { S.push(e); low[u]=min(low[u],pre[v]); } } if(father==-1&&child==1) iscut[u]=false; return low[u]; } void findbcc(int n) { memset(pre,0,sizeof pre); memset(low,0,sizeof low); memset(iscut,false,sizeof iscut); memset(bccno,0,sizeof bccno); dfs_clock=bcc_cnt=0; for(int i=0;i<n;i++) if(!pre[i]) DFS(i,-1); } int Case; int n; int main() { while(scanf("%d",&n)==1&&n) { for(int i=0;i<2*n;i++) G[i].clear(); for(int i=1;i<=n;i++) { int u,v; scanf("%d%d",&u,&v); u--;v--; G[u].push_back(v); G[v].push_back(u); } findbcc(n); 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,size=bcc[i].size();j<size;j++) if(iscut[bcc[i][j]]) cut_cnt++; if(cut_cnt==1) { ans1++; ans2*=(LL)(bcc[i].size()-1); } } printf("Case %d: %lld %lld\n",++Case,ans1,ans2); } return 0; } /* 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 */