一道无向图tarjan的题
大致思路是先找到割点,然后给连通块染色
分三种情况:
如果连通块有一个割点,那么建在哪都行
如果有两个割点那么无需建出口,因为任意一个割点倒塌都可以从另一个割点跑到另一个连通块
如果没有割点的话则无法跑到其他连通块,在除割点的任意两点建两个连通块
#include<iostream> #include<cstdio> #include<cstring> #define mx 505 using namespace std; struct edge{ int to,nxt; }ed[2*mx]; int head[mx],ecnt; void addedge(int u,int v){ ed[++ecnt].to=v; ed[ecnt].nxt=head[u]; head[u]=ecnt; } long long root,mark,cnt,siz,numcase; long long low[mx],dfn[mx],num; long long col,color[mx]; bool dot[mx]; void tarjan(int u,int fa){ low[u]=dfn[u]=++num; for(int i=head[u];i;i=ed[i].nxt){ int v=ed[i].to; if(!dfn[v]){ tarjan(v,u); low[u]=min(low[u],low[v]); if(low[v]>=dfn[u]){ if(u==root) mark++; else dot[u]=1; } } else if(v!=fa) low[u]=min(low[u],dfn[v]); } } void get_col(int u){ color[u]=col; siz++; for(int i=head[u];i;i=ed[i].nxt){ int v=ed[i].to; if(color[v]!=col&&dot[v]){ cnt++; color[v]=col; } if(!color[v]) get_col(v); } } long long ans1,ans2; void init(){ memset(head,0,sizeof head); memset(ed,0,sizeof ed); memset(color,0,sizeof color); memset(dfn,0,sizeof dfn); memset(low,0,sizeof low); memset(dot,0,sizeof dot); col=mark=ans1=0; ans2=1; } int main(){ int n=0,m; while(scanf("%d",&m)&&m){ n=0; numcase++; init();//多组数据初始化 for(int i=1;i<=m;i++){ int s,t; scanf("%d%d",&s,&t); addedge(s,t); addedge(t,s); n=max(n,max(s,t));//此处求点的数量 } for(int i=1;i<=n;i++){ if(!dfn[i]) { mark=0; tarjan(root=i,-1); if(mark>=2) dot[root]=1; } } for(int i=1;i<=n;i++) if(!color[i]&&!dot[i]) { cnt=0,siz=0;//cnt表示割点数量,siz表示非割点数量 col++; get_col(i); if(cnt==1){ ans1+=1; ans2*=siz; } if(cnt==0){ ans1+=2; ans2*=((siz-1)*siz)/2;//除以2是因为选择a,b和b,a是等价的 } } printf("Case %lld: %lld %lld\n",numcase,ans1,ans2);//注意数据范围是long long } return 0; }