【算法】点双连通分量
【题解】详见《算法竞赛入门竞赛入门经典训练指南》P318-319
细节在代码中用important标注。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include<cstdio> #include<algorithm> #include<vector> #include<stack> #include<cstring> using namespace std; const int maxm=50010; struct edge{int u,v,from;}e[maxm*3]; int first[maxm],iscut[maxm],dfn[maxm],low[maxm],bccno[maxm],bcc_cnt,dfsnum,tot,n,m,kase; vector<int>bcc[maxm]; stack<int>s; void insert(int u,int v) {tot++;e[tot].u=u;e[tot].v=v;e[tot].from=first[u];first[u]=tot;} void tarjan(int x,int fa) { dfn[x]=low[x]=++dfsnum; int child=0; for(int i=first[x];i;i=e[i].from) if(e[i].v!=fa) { int y=e[i].v; // s.push(i); if(!dfn[y]) { s.push(i);// child++; //important tarjan(y,x); low[x]=min(low[x],low[y]); if(low[y]>=dfn[x]) { iscut[x]=1; bcc_cnt++; bcc[bcc_cnt].clear(); for(;;) { int now=s.top();s.pop(); int u=e[now].u,v=e[now].v; if(bccno[u]!=bcc_cnt){bcc[bcc_cnt].push_back(u);bccno[u]=bcc_cnt;} if(bccno[v]!=bcc_cnt){bcc[bcc_cnt].push_back(v);bccno[v]=bcc_cnt;} // printf("%d\n",bcc_cnt); if(u==x&&v==y)break; } } } else if(dfn[y]<dfn[x])s.push(i),low[x]=min(low[x],dfn[y]);//important } if(fa<0&&child==1)iscut[x]=0; } int main() { scanf("%d",&m); while(m!=0) { memset(first,0,sizeof(first)); n=0;tot=0;bcc_cnt=0; for(int i=1;i<=m;i++) { int u,v; scanf("%d%d",&u,&v); n=max(n,max(u,v)); insert(u,v); insert(v,u); } memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(iscut,0,sizeof(iscut)); memset(bccno,0,sizeof(bccno)); dfsnum=0; while(!s.empty())s.pop(); for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i,-1); long long ans1=0,ans2=1; 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*=(long long)(bcc[i].size()-cut_cnt); } } if(bcc_cnt==1) { ans1=2; ans2=bcc[1].size()*(bcc[1].size()-1)/2; } printf("Case %d: %lld %lld\n",++kase,ans1,ans2); scanf("%d",&m); } return 0; }