题目链接
题意:
对于联通图中加入一条边后,图中的最少桥的数量
解题思路:
先把边双联通分量缩出来,形成一颗树,那么树的边就全是桥,现在可以加一条边,消掉尽可能 多的桥,那么直接用树上最远的两个点就可以消掉最多树的直径个桥。
#include<stdlib.h>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<queue>
#include<time.h>
#include <cstdio>
#include <iostream>
#include <vector>
#define Re int
using namespace std;
const int N=200010,M=2e6+60000;
int n,m,x,y,Q_o,leaf,ip[N],du[N];
inline void in(Re &x)
{
int f=0;
x=0;
char c=getchar();
while(c<'0'||c>'9')
f|=c=='-',c=getchar();
while(c>='0'&&c<='9')
x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=f?-x:x;
}
struct Tarjan
{
int o,dfn_o,dfn[N],low[N],head[N],bridge[M<<1];
struct QAQ
{
int to,next;
} a[M<<1];
inline void add(Re x,Re y)
{
a[++o].to=y,a[o].next=head[x],head[x]=o;
}
inline void cle()
{
o=1;
dfn_o=0;
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(head,0,sizeof(head));
memset(bridge,0,sizeof(bridge));
Q_o=0;
}
inline void tarjan(Re x,Re p)
{
dfn[x]=low[x]=++dfn_o;
for(Re i=head[x],to; i; i=a[i].next)
if(!dfn[to=a[i].to])
{
tarjan(to,i);
low[x]=min(low[x],low[to]);
if(low[to]>dfn[x])
bridge[i]=bridge[i^1]=1;
}
else if(i!=(p^1))
low[x]=min(low[x],dfn[to]);
}
inline void dfs(Re x)
{
ip[x]=Q_o;
for(Re i=head[x]; i; i=a[i].next)
if(!ip[a[i].to]&&!bridge[i])
dfs(a[i].to);
}
inline void SuoPoint()
{
for(Re i=1; i<=n; ++i)
if(!dfn[i])
tarjan(i,-1);
for(Re i=1; i<=n; ++i)
if(!ip[i])
++Q_o,dfs(i);
}
} T1;
vector<int>G[200001];
int d[N];
int c;
void dfs(int u, int fa)
{
for (int v : G[u])
{
if (v == fa)
continue;
d[v] = d[u] + 1;
if (d[v] > d[c])
c = v;
dfs(v, u);
}
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
if(n<=0&&m<=0)
{
break;
}
memset(ip,0,sizeof(ip));
memset(du,0,sizeof(du));
T1.o=1;
T1.cle();
while(m--)
in(x),in(y),T1.add(x,y),T1.add(y,x);
T1.SuoPoint();
leaf=0;
for(Re i=2; i<=T1.o; i+=2)
if((x=ip[T1.a[i].to])!=(y=ip[T1.a[i^1].to]))
{
G[x].push_back(y);
G[y].push_back(x);
}
dfs(1,0);
d[c] = 0, dfs(c, 0);
printf("%d\n", (Q_o-1)-d[c]);
memset(d,0,sizeof(d));
for(int i=0; i<=Q_o; i++)
{
G[i].clear();
}
}
}