无向图的双联通分量

无向图的双连通分量

定义:若一张无向连通图不存在割点,则称它为"点双连通图"。若一张无向连通图不存在割边,则称它为"边双连通图"

无向图图的极大点双连通子图被称为"点双连通分量",记为 " v − D C C " "v−DCC" "vDCC"。无向图图的极大边双连通子图被称为**“边双连通分量”**,记为 " e − D C C " 。 "e−DCC"。 "eDCC"

边双连通分量
核心概念:没有割边的无向连通图。
注意到,割边只会把图分成两部分,对图中的点没有影响。那么有一个显而易见的求法就是:利用Tarjan算法求解无向图的割边,并将割边去除,得到的各个连通块即为边双连通分量。

模 板 : 模板:

void Tarjan(int x,int fa)
{
    dfn[x]=low[x]=++Time;
    for(int i=head[x];i!=-1;i=e[i].next)
    {
        int y=e[i].to;
        if(y==fa) continue;
        if(!dfn[y])
        {
            Tarjan(y,x);
            low[x]=min(low[x],low[y]);
            if(low[y]>dfn[x]) bri[i]=bri[i^1]=true;
        }
        else low[x]=min(low[x],dfn[y]);
    }
}
void dfs(int x) // 相当于对每个双连通分量染色
{
    col[x]=cnt;
    for(int i=head[x];i!=-1;i=e[i].next)
    {
        int y=e[i].to;
        if(bri[i]) continue;
        if(!col[y]) dfs(y);
    }
}

其实 T a r j a n Tarjan Tarjan后也可以不用对每个连通块进行染色, l o w low low数组相当于已经染色了, l o w low low相同则两点在同一双连通分量

Road Construction

图删去一条边之后不影响连通性,就是双连通图的概念了,这题就是要让你添加最少的边使图变成双连通图,做法就是先找出所有边双联通块(同一个双连通块内的点的low值是相等的),把连通块看成一个点,整张图就变成一颗树,问题转化成如何把这棵树变成双连通图,扫一遍统计所有结点的度数,度数为1的结点就需要加边,加边优先和其他度数为1的点相连,最后需要添加的边就是(度数为1的点的数量+1)/2。

#include <set>
#include <map>
#include <list>
#include <cmath>
#include <stack>
#include <queue>
#include <string>
#include <bitset>
#include <vector>
#include<cstring>
#include <stdio.h>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
#define lowbit(x)  ((x)&-(x))
typedef unsigned long long ull;
#define freedom() ios::sync_with_stdio(false);
inline int read(){int s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();return s*w;}
const int maxn = 1e4+5;
int tot,head[maxn];
struct Edge{
    int to,next;
}e[maxn*10];
int cnt,col[maxn];
int dfn[maxn],low[maxn],Time;
void add_e(int u,int v)
{
    e[tot]={v,head[u]};
    head[u]=tot++;
}
bool bri[maxn*10];
int ans;
void Tarjan(int x,int fa)
{
    dfn[x]=low[x]=++Time;
    for(int i=head[x];i!=-1;i=e[i].next)
    {
        int y=e[i].to;
        if(y==fa) continue;
        if(!dfn[y])
        {
            Tarjan(y,x);
            low[x]=min(low[x],low[y]);
            if(low[y]>dfn[x]) bri[i]=bri[i^1]=true;
        }
        else low[x]=min(low[x],dfn[y]);
    }
}
void dfs(int x)
{
    col[x]=cnt;
    for(int i=head[x];i!=-1;i=e[i].next)
    {
        int y=e[i].to;
        if(bri[i]) continue;
        if(!col[y]) dfs(y);
    }
}
int In[maxn];
int solve(int n)
{
    ans=0;
    for(int i=1;i<=n;i++)
        if(!dfn[i]) Tarjan(i,i);
    for(int i=1;i<=n;i++)
        if(!col[i]) ++cnt,dfs(i);
    for(int i=1;i<=n;i++)
        for(int k=head[i];k!=-1;k=e[k].next)
        {
            int u=i,v=e[k].to;
            if(col[u]!=col[v]) In[col[v]]++;
        }
    for(int i=1;i<=cnt;i++)
        if(In[i]==1) ans++;
    return (ans+1)/2;
}
void init()
{
    Time=0,tot=0,cnt=0;
    memset(In,0,sizeof(In));
    memset(head,-1,sizeof(head));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(col,0,sizeof(col));
    memset(bri,0,sizeof(bri));
}
int main()
{
    int n=read(),m=read();
    init();
    for(int i=1;i<=m;i++)
    {
        int u=read(),v=read();
        add_e(u,v);
        add_e(v,u);
    }
    printf("%d\n",solve(n));
    return 0;
}

或者

#include <set>
#include <map>
#include <list>
#include <cmath>
#include <stack>
#include <queue>
#include <string>
#include <bitset>
#include <vector>
#include<cstring>
#include <stdio.h>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
#define lowbit(x)  ((x)&-(x))
typedef unsigned long long ull;
#define freedom() ios::sync_with_stdio(false);
inline int read(){int s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();return s*w;}
const int maxn = 1e4+5;
int tot,head[maxn];
struct Edge{
    int to,next;
}e[maxn*10];
int cnt,col[maxn];
int dfn[maxn],low[maxn],Time;
void add_e(int u,int v)
{
    e[tot]={v,head[u]};
    head[u]=tot++;
}
bool bri[maxn*10];
int ans;
void Tarjan(int x,int fa)
{
    dfn[x]=low[x]=++Time;
    for(int i=head[x];i!=-1;i=e[i].next)
    {
        int y=e[i].to;
        if(y==fa) continue;
        if(!dfn[y])
        {
            Tarjan(y,x);
            low[x]=min(low[x],low[y]);
            if(low[y]>dfn[x]) bri[i]=bri[i^1]=true;
        }
        else low[x]=min(low[x],dfn[y]);
    }
}
void dfs(int x)
{
    col[x]=cnt;
    for(int i=head[x];i!=-1;i=e[i].next)
    {
        int y=e[i].to;
        if(bri[i]) continue;
        if(!col[y]) dfs(y);
    }
}
int In[maxn];
int solve(int n)
{
    ans=0;
    for(int i=1;i<=n;i++)
        if(!dfn[i]) Tarjan(i,i);
    for(int i=1;i<=n;i++)
        if(!col[i]) ++cnt,dfs(i);
    for(int i=1;i<=n;i++)
        for(int k=head[i];k!=-1;k=e[k].next)
        {
            int u=i,v=e[k].to;
            if(low[u]!=low[v]) In[low[v]]++;
        }
    for(int i=1;i<=n;i++)
        if(In[i]==1) ans++;
    return (ans+1)/2;
}
void init()
{
    Time=0,tot=0,cnt=0;
    memset(In,0,sizeof(In));
    memset(head,-1,sizeof(head));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(col,0,sizeof(col));
    memset(bri,0,sizeof(bri));
}
int main()
{
    int n=read(),m=read();
    init();
    for(int i=1;i<=m;i++)
    {
        int u=read(),v=read();
        add_e(u,v);
        add_e(v,u);
    }
    printf("%d\n",solve(n));
    return 0;
}

397. 逃不掉的路

对于树,我们显然使用树上点距的方法来求解。dis(x,y)=depthx+depthy−2depthlca(x,y)。lca用树上倍增求解就可以了,这些都是模板,就不过多讨论了。

那么这是无向图呢,e−DCC缩点构树就可以了。

为什么不用v−DCC?v−DCC比较特殊,由于割点同时被多个点双连通分量包含,所以缩点需要保留割点,会增加点的数量,一般模型中,我们使用e−DCC缩点。

#include <set>
#include <map>
#include <list>
#include <cmath>
#include <stack>
#include <queue>
#include <string>
#include <bitset>
#include <vector>
#include<cstring>
#include <stdio.h>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
#define lowbit(x)  ((x)&-(x))
typedef unsigned long long ull;
#define freedom() ios::sync_with_stdio(false);
inline int read(){int s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();return s*w;}
const int maxn = 5e5+5;
int tot,head[maxn];
int Head[maxn],Tot;
struct Edge{
    int to,next;
}e[maxn],E[maxn];
int pre[maxn],id[maxn];
int cnt,col[maxn];
int U[maxn],V[maxn];
int dis[maxn],lca[maxn];
bool vis[maxn],bri[maxn<<2];
int dfn[maxn],low[maxn],Time;
void init(){
    memset(Head,-1,sizeof(Head));
    memset(head,-1,sizeof(head));
}
void add_e(int u,int v,int Id){
    e[tot]={v,head[u]};id[tot]=Id;head[u]=tot++;
}
void add_E(int u,int v){
    E[Tot]={v,Head[u]};Head[u]=Tot++;
}

void Tarjan(int x,int fa)
{
    dfn[x]=low[x]=++Time;
    for(int i=head[x];i!=-1;i=e[i].next)
    {
        int y=e[i].to;
        if(y==fa) continue;
        if(!dfn[y])
        {
            Tarjan(y,x);
            low[x]=min(low[x],low[y]);
            if(low[y]>dfn[x]) bri[i]=bri[i^1]=true;
        }
        else low[x]=min(low[x],dfn[y]);
    }
}

void dfs(int x)
{
    col[x]=cnt;
    for(int i=head[x];i!=-1;i=e[i].next)
    {
        int y=e[i].to;
        if(bri[i]) continue;
        if(!col[y]) dfs(y);
    }
}

int find(int x)
{
    if(x!=pre[x]) pre[x]=find(pre[x]);
    return pre[x];
}

void TarjanLca(int x)
{
    vis[x]=true;
    pre[x]=x;
    for(int i=Head[x];i!=-1;i=E[i].next)
    {
        int y=E[i].to;
        if(vis[y]) continue;
        dis[y]=dis[x]+1;
        TarjanLca(y);
        pre[y]=x;
    }
    for(int i=head[x];i!=-1;i=e[i].next)
    {
        int y=e[i].to,Id=id[i];
        if(vis[y]) lca[Id]=find(y);
    }
}

void solve(int n)
{
    for(int i=1;i<=n;i++)
        if(!dfn[i]) Tarjan(i,i);
    for(int i=1;i<=n;i++)
        if(!col[i]) ++cnt,dfs(i);

    for(int i=1;i<=n;i++)
        for(int k=head[i];k!=-1;k=e[k].next)
        {
            int u=i,v=e[k].to;
            if(col[u]!=col[v]) add_E(col[u],col[v]),add_E(col[v],col[u]);
        }
}

void Output()
{
    int q=read();
    tot=0;
    memset(head,-1,sizeof(head));
    for(int i=1;i<=q;i++)
    {
        int u=read(),v=read();
        U[i]=col[u],V[i]=col[v];
        add_e(col[u],col[v],i),add_e(col[v],col[u],i);
    }
    TarjanLca(1);
    for(int i=1;i<=q;i++)
        printf("%d\n",dis[U[i]]+dis[V[i]]-2*dis[lca[i]]);
}

int main()
{
    int n=read(),m=read();
    init();
    for(int i=1;i<=m;i++)
    {
        int u=read(),v=read();
        add_e(u,v,0);
        add_e(v,u,0);
    }
    solve(n);
    Output();
    return 0;
}

倍 增 法 倍增法
c o d e : code : code:

#include <set>
#include <map>
#include <list>
#include <cmath>
#include <stack>
#include <queue>
#include <string>
#include <bitset>
#include <vector>
#include<cstring>
#include <stdio.h>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
#define lowbit(x)  ((x)&-(x))
typedef unsigned long long ull;
#define freedom() ios::sync_with_stdio(false);
inline int read(){int s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();return s*w;}
const int maxn = 2e5+5;
int tot,head[maxn];
int Head[maxn],Tot;
struct Edge{
    int to,next;
}e[maxn],E[maxn];
int pre[maxn];
int cnt,col[maxn];
bool vis[maxn];
int dfn[maxn],low[maxn],Time;
int id[maxn]; bool bri[maxn<<2];
void init(){
    for(int i=0;i<maxn;i++) Head[i]=head[i]=-1;
}
void add_e(int u,int v){
    e[tot]={v,head[u]};head[u]=tot++;
}
void add_E(int u,int v){
    E[Tot]={v,Head[u]};Head[u]=Tot++;
}
int n,m;
void Tarjan(int x,int fa)
{
    dfn[x]=low[x]=++Time;
    for(int i=head[x];i!=-1;i=e[i].next)
    {
        int y=e[i].to;
        if(y==fa) continue;
        if(!dfn[y])
        {
            Tarjan(y,x);
            low[x]=min(low[x],low[y]);
            if(low[y]>dfn[x]) bri[i]=bri[i^1]=true;
        }
        else low[x]=min(low[x],dfn[y]);
    }
}

void dfs(int x)
{
    col[x]=cnt;
    for(int i=head[x];i!=-1;i=e[i].next)
    {
        int y=e[i].to;
        if(bri[i]) continue;
        if(!col[y]) dfs(y);
    }
}
int U[maxn],V[maxn];
void solve()
{
    for(int i=1;i<=n;i++)
        if(!dfn[i]) Tarjan(i,i);

    for(int i=1;i<=n;i++)
        if(!col[i]) ++cnt,dfs(i);

//    for(int i=1;i<=n;i++)
//        for(int k=head[i];k!=-1;k=e[k].next)
//        {
//            int u=i,v=e[k].to;
//            if(col[u]!=col[v]) add_E(col[u],col[v]),add_E(col[v],col[u]);
//        }
    for(int i=1;i<=m;i++)
    {
        if(col[U[i]]!=col[V[i]])
            add_E(col[U[i]],col[V[i]]),add_E(col[V[i]],col[U[i]]);
    }
}

int dep[maxn],f[maxn][100],lg[maxn];

void dfss(int x,int fa)
{
    f[x][0]=fa,dep[x]=dep[fa]+1;

    for(int i=1;i<=lg[dep[x]];i++)
        f[x][i]=f[f[x][i-1]][i-1];

//    for(int i=1;(1<<i)<=dep[x];i++)
//        f[x][i]=f[f[x][i-1]][i-1];

    for(int i=Head[x];i!=-1;i=E[i].next)
        if(E[i].to!=fa) dfss(E[i].to,x);
}

int LCA(int x,int y)
{
    if(dep[x]<dep[y]) swap(x,y);

    while (dep[x]>dep[y]) x=f[x][lg[dep[x]-dep[y]]-1];

    if(x==y) return x;

    for(int i=lg[dep[x]]-1;i>=0;i--)
        if(f[x][i]!=f[y][i])
            x=f[x][i],y=f[y][i];

    return f[x][0];
}
void Query()
{
      for(int i=1;i<=cnt;i++) lg[i]=lg[i-1]+(1<<lg[i-1]==i);

      dfss(1,0);
      int q=read();
      for(int i=1;i<=q;i++)
      {
          int u=read(),v=read();

          printf("%d\n",dep[col[u]]+dep[col[v]]-2*dep[LCA(col[u],col[v])]);
      }
}

int main()
{
    n=read(),m=read();
    init();
    for(int i=1;i<=m;i++)
    {
        int u=read(),v=read();
        U[i]=u,V[i]=v;
        add_e(u,v);
        add_e(v,u);
    }
    solve();
    Query();
    return 0;
}

点双连通分量#
核心概念:没有割点的无向连通图。
这里给出定理,无向连通图是"点双连通图",当且仅当满足下列两个条件之一:

图的顶点不超过2个。
图中任意两个点都同时包含在一个简单环中。"简单环"指的是不相交的环。

点双连通分量的求法:

若某个点为“孤立点”,这个点肯定是点双。
其他的点双连通分量大小至少为2个点。

与强联通分量类似,用一个栈来维护,如果这个点第一次被访问时,把该节点进栈。当割点判定法则中的条件 dfnx≤lowy时,无论x是否为根,都要从栈顶不断弹出节点,直至节点y被弹出,刚才弹出的所有节点与节点x一起构成一个点双连通分量。

int dfn[maxn],low[maxn],id,t;
edge zhan[(maxn*maxn)<<1];//存边的栈
int belong[maxn],cnt;//belong记录每个点属于哪一个点双,cnt记录点双个数
bool cut[maxn];
set<int> s[maxn];//记录每个点双包含哪些点,如果题目不需要也可以不求
void dfs(int x,int from)
{
	dfn[x]=low[x]=++id; int son=0;
	for(int i=first[x];i;i=e[i].next)
	{
		if(i==(from^1))continue;
		int y=e[i].y;
		if(!dfn[y])
		{
			zhan[++t]=e[i]; dfs(y,i); son++;//先压栈再遍历
			if(low[y]<low[x])low[x]=low[y];
			if(low[y]>=dfn[x])//发现x是割点
			{
				cnt++; edge xx; cut[x]=true;
				do{
					xx=zhan[t--];//弹出
					belong[xx.x]=belong[xx.y]=cnt;//标记
					s[cnt].insert(xx.x);s[cnt].insert(xx.y);//记录
				}while(xx.x!=x||xx.y!=y);//假如已经弹到 x到y 这条边了,就停下来
			}
		}
		else if(dfn[y]<low[x])low[x]=dfn[y];
	}
	if(from==-1&&son==1)cut[x]=false;
}

P3225 [HNOI2012]矿场搭建

对于每一个发生事故的点,可能会将原图分为几个联通块,使得我们需要在不同的联通块设置更多的救援出口,这就和我们割点模型有些相似了。

进一步地,我们需要求出图中的每一个割点和各个点双连通分量,对于每一个点双连通分量,如果当中包含了一个割点,则说明这当中我们需要在非割点位置设置一个救援出口(如果割点位置发生事故,该连通块会被独立,必须额外独立设置一个救援出口),出口数累加,方案数按照点双连通分量大小减去一(一个割点不能放)累乘即可。如果当中包含了两个割点,则说明无论什么情况这一块都不会被"独立",不需要设置救援出口。

最后,还有一种特殊情况需要判断:若整张图就是一个点双连通分量,即没有割点,那么我们至少设置两个出口(若设置一个,可能恰好在出口处发生事故),方案数为n(n−1)2。

算法流程:
先tarjan求一下所有的点双连通分量。然后对于每一个点双连通分量,分类讨论:

只有一个割点,必须选一个非割点。

有多于1个割点,不用选

没有个割点,必须选两个。

#include <set>
#include <map>
#include <list>
#include <cmath>
#include <stack>
#include <queue>
#include <string>
#include <bitset>
#include <vector>
#include<cstring>
#include <stdio.h>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
typedef unsigned long long ull;
inline int read(){int s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();return s*w;}
const int maxn = 1e3+5;
int tot,head[maxn];
int dnf[maxn],low[maxn],Time;
int n,m,cnt,cas;
stack<int>sk;
bool col[maxn],vis[maxn];
vector<int>vec[1005];
struct Edge{
    int to,next;
}edge[maxn<<2];
void add_edge(int u,int v)
{
    edge[tot]={v,head[u]};
    head[u]=tot++;
}
void init()
{
    n=0,tot=0,Time=0,cnt=0;
    memset(vis,0,sizeof(vis));
    while (!sk.empty()) sk.pop();
    memset(head,-1,sizeof(head));
    memset(dnf,0,sizeof(dnf));
    memset(low,0,sizeof(low));
    memset(col,0,sizeof(col));
    for(int i=0;i<maxn;i++) vec[i].clear();
}
void Tarjan(int x,int fa)
{
   dnf[x]=low[x]=++Time;
   sk.push(x);
   int Fa=0;
   if(x==fa&&head[x]==-1)
   {
       cnt++;
       sk.pop();
       vec[cnt].push_back(x);
       return;
   }
   for(int i=head[x];i!=-1;i=edge[i].next)
   {
       int y=edge[i].to;
       if(y==fa) continue;
       if(!dnf[y])
       {
           Tarjan(y,x);
           low[x]=min(low[x],low[y]);
           if(low[y]>=dnf[x])
           {
               ++cnt;
               int top=0;
               while(top!=y)
               {
                   top=sk.top();
                   sk.pop();
                   vec[cnt].push_back(top);
               }
               vec[cnt].push_back(x);
               Fa++;
               if(x!=fa||Fa>1) col[x]=true;
           }
       }
       else low[x]=min(low[x],dnf[y]);
   }
}
void solve()
{
    ll ans=0,Ans=1;
    if(cnt==1) {printf("Case %d: %d %d\n",++cas,2,n*(n-1)/2);return;}
    for(int i=1;i<=cnt;i++)
    {
        int g=0;
        for(int k=0;k<vec[i].size();k++)
            if(col[vec[i][k]]) g++;
        if(g==1) ans+=1,Ans*=(vec[i].size()-1);
        else if(g>1) continue;
    }
    printf("Case %d: %lld %lld\n",++cas,ans,Ans);
}
void Input()
{
    for(int i=1;i<=m;i++)
    {
        int u=read(),v=read();
        if(!vis[u]) n++;
        if(!vis[v]) n++;
        vis[u]=vis[v]=true;
        add_edge(u,v);add_edge(v,u);
    }
    for(int i=1;i<=n;i++)
        if(!dnf[i]) Tarjan(i,i);
}
int main()
{
    while (~scanf("%d",&m)&&m)
    {
        init();
        Input();
        solve();
    }
    return 0;
}

边 双 : 处 理 重 边 : 板 子 边双:处理重边:板子

HDU - 4612 Warm up

#include <set>
#include <map>
#include <list>
#include <cmath>
#include <stack>
#include <queue>
#include <string>
#include <bitset>
#include <vector>
#include<cstring>
#include <stdio.h>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
typedef unsigned long long ull;
inline int read(){int s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();return s*w;}
const int maxn = 2e5+5;
int head[maxn],tot;
int dfn[maxn],low[maxn],Time;
bool bri[maxn*10];
int col[maxn],cnt;
int n,m;
struct E{
    int to,next;
}e[maxn*10];
int cut;
int dp[maxn];
void add_e(int u,int v){
    e[tot]={v,head[u]};
    head[u]=tot++;
}
void init(){
    tot=0;
    Time=cnt=0; cut=0;
    memset(bri,false,sizeof(bri));
    for(int i=0;i<maxn;i++)
        head[i]=-1,dfn[i]=low[i]=col[i]=0,dp[i]=0;
}
void Tarjan(int x,int fa){
    dfn[x]=low[x]=++Time;
    for(int i=head[x];i!=-1;i=e[i].next){
        int y=e[i].to;
//        if(y==fa) continue;
        if(!dfn[y]){
            Tarjan(y,i),low[x]=min(low[x],low[y]);
            if(low[y]>dfn[x]) bri[i]=bri[i^1]=true,cut++;
        }
        else if(i!=(fa^1)) low[x]=min(low[x],dfn[y]);
    }
}
void dfs(int x){
    col[x]=cnt;
    for(int i=head[x];i!=-1;i=e[i].next){
        int y=e[i].to;
        if(col[y]||bri[i]) continue;
        dfs(y);
    }
}
int U[maxn*10],V[maxn*10];
void Dfs(int x,int fa){
    for(int i=head[x];i!=-1;i=e[i].next){
        int y=e[i].to;
        if(y==fa) continue;
        dp[y]=dp[x]+1;
        Dfs(y,x);
    }
}
int main()
{
    while (~scanf("%d%d",&n,&m)&&n&&m){
        init();
        for(int i=1;i<=m;i++){
            int u=read(),v=read();
            U[i]=u,V[i]=v;
            add_e(u,v),add_e(v,u);
        }
        for(int i=1;i<=n;i++) if(!dfn[i]) Tarjan(i,i);
  //      printf("%d\n",cut);
        for(int i=1;i<=n;i++) if(!col[i]) ++cnt,dfs(i);
        tot=0;
        memset(head,-1,sizeof(head));
        for(int i=1;i<=m;i++){
            int u=col[U[i]],v=col[V[i]];
            if(u==v) continue;
            add_e(u,v),add_e(v,u);
        }
        Dfs(1,0);
        int Max=-1,pos=0;
        for(int i=1;i<=cnt;i++)
            if(Max<dp[i]) Max=dp[i],pos=i;
        memset(dp,0,sizeof(dp));
        Dfs(pos,0);
        Max=-1;
        for(int i=1;i<=cnt;i++)
            Max=max(Max,dp[i]);
        int ans=cut-Max;
        printf("%d\n",ans);

    }
    return 0;
}
/*
3 3
1 2
2 1
2 3
 */
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值