正题
点双连通分量??边双连通分量??
不懂??
其实这题也没那么麻烦,代码也没那么长~
我们先找割点,然后分类讨论一下。
我们dfs找出与当前节点(枚举)在同一连通块的点,如果是割点就不用往下搜了,那么我们求出来的就是,割点被炸掉之后的连通分块。
当 当前连通分块的割点有两个及以上时,那么无论炸掉那个点,该连通分块的点都可以通过另一割点去到另一连通分块。
当 当前连通分块的割点有一个时,那么如果炸掉的是割点,该连通分块的点出不去了,就在这个连通分块建一个出口。
当 当前连通分块没有割点,那么我们就要建两个出口,这样就可以使得无论炸掉那个点,另外的节点都有出口。
方案算一下即可,当然不能出口不能设在割点。
扫描二维码关注公众号,回复:
182839 查看本文章
#include<cstdio> #include<cstdlib> #include<cstring> #include<stack> using namespace std; int m; int n; struct edge{ int x,y,next; }s[10010]; struct node{ int dfn,low; }op[10010]; int first[10010]; int len=0; int now=0; bool cut[10010]; long long ans1,ans2=1; int vis[10010]; int T; int num=0; long long cnt=0; int nn=0; void ins(int x,int y){ len++; s[len].x=x;s[len].y=y;s[len].next=first[x];first[x]=len; } void Tarjan(int x,int fa){ op[x].dfn=op[x].low=++now; int son=0; for(int i=first[x];i!=0;i=s[i].next){ int y=s[i].y; if(op[y].dfn==0){ Tarjan(y,x); if(op[x].dfn<=op[y].low) {//找割点 cut[x]=1; son++; } if(op[y].low<op[x].low) op[x].low=op[y].low; } else if(op[y].dfn<op[x].low) op[x].low=op[y].dfn; } if(fa==0 && son<2) cut[x]=0; } void dfs(int x){ vis[x]=T;cnt++; for(int i=first[x];i!=0;i=s[i].next){ int y=s[i].y; if(vis[y]!=T && cut[y]) num++,vis[y]=T;//如果是割点,不用往下搜。 if(!vis[y] && !cut[y]) dfs(y);//如果不是割点,继续往下搜。 } } int main(){ while(1){ nn++;T=0;len=0;now=0; ans1=0,ans2=1; memset(op,0,sizeof(op)); memset(first,0,sizeof(first)); memset(cut,false,sizeof(cut)); memset(vis,0,sizeof(vis)); scanf("%d",&m); if(m==0) break; n=0; for(int i=1;i<=m;i++){ int x,y; scanf("%d %d",&x,&y); ins(x,y);ins(y,x); n=max(n,max(x,y)); } for(int i=1;i<=n;i++) if(op[i].dfn==0) Tarjan(i,0); for(int i=1;i<=n;i++){ if(!vis[i] && !cut[i]){ num=0,cnt=0;T++;//num记录的是当前连通分块中的割点数量,cnt是当前连通分块其他点的点数 dfs(i); if(num==0) ans1+=2,ans2*=cnt*(cnt-1)/2;//组合数计算 if(num==1) ans1+=1,ans2*=cnt; } } printf("Case %d: %lld %lld\n",nn,ans1,ans2); } }