题意:n个点 m条边(下面m行 a->b 表示单向边)
问:使图变成强连通需要增加几条边
思路:
把图变成树(树中每条边都是桥,再计算连通分支之间需要的边)
连通分支用tarjan缩点,然后统计连通分支的度(这里统计的方法有点暴力,可以用并查集统计)
补充:
桥:去掉桥后两边不连通(即 图去掉这条边后变成2块 的边 叫做桥)
连通分支: 上面说的块 就是连通分支
#include <stdio.h> #include <string.h> #include <iostream> using namespace std; #define N 20010 int Low[N],dfn[N],stack[N],top,head[N]; bool Ins[N]; int F[N]; int Find(int x){if(x==F[x])return x;return F[x]=Find(F[x]);} int en,time,kuai,K[N];//k[x] x点属于的块 kuai用来计数图中块的数量 time是遍历图的走的步数 struct node{ int f,t,pre; }edge[N*3];//en 是边数 inline int Min(int a,int b){return a>b?b:a;} void add(int a,int b){ edge[en].f=a,edge[en].t=b;edge[en].pre=head[a]; head[a]=en; en++; } void Tarjan(int u){ dfn[u]=Low[u]=++time; stack[++top]=u; Ins[u]=true; for(int i=head[u];i!=-1;i=edge[i].pre) { int v=edge[i].t; if(dfn[v]==-1) { Tarjan(v); Low[u]=Min(Low[u],Low[v]); } else if(Ins[v]) Low[u]=Min(Low[u],dfn[v]); } if(Low[u]==dfn[u]) { ++kuai; int v; do{ v=stack[top--]; Ins[v]=false; K[v]=kuai; if(u<v) F[u]=Find(F[v]); else F[v]=Find(F[u]); }while(v!=u); } } void Tsolve(int n){ memset(dfn,-1,sizeof(dfn)); time=top=kuai=0; for(int i=1;i<=n;i++)if(dfn[i]==-1)Tarjan(i); } int chudu[N],rudu[N]; int zouni(int n) { if(kuai<=1)return 0; memset(chudu,0,sizeof(chudu)); memset(rudu,0,sizeof(rudu)); for(int i=1;i<=n;i++) { int u=K[i]; for(int j=head[i];j!=-1;j=edge[j].pre) { int v=K[edge[j].t]; if(u!=v) chudu[v]++,rudu[u]++; } } int sum1 = 0,sum2 = 0; for (int i = 1; i <= kuai; i ++) if(!chudu[i]) sum1 ++; for (int i = 1; i <= kuai; i ++) if(!rudu[i]) sum2 ++; return sum1 > sum2 ? sum1 : sum2; } int main() { int n,i,m,T;scanf("%d",&T); while(T--) { scanf("%d %d",&n,&m); en=0; memset(head,-1,sizeof(head)); for(i=1;i<=n;i++)F[i]=i; while(m--){ int a,b;scanf("%d%d",&a,&b); add(a,b);} Tsolve(n);//共n个点(1-n) printf("%d\n",zouni(n)); } return 0; } /* 99 5 5 1 2 2 3 3 4 4 1 1 3 ans:2 */