题意:
一个有重边的无向连通图,添加一条边后最少剩几个桥。2 <= n <= 200000 , 1 <= m <= 1000000。
题解:
1.先用Tarjan缩点后求出桥数,然后原图就变成了一个树,对于一颗树,添加1条边后剩的最少桥数 = 总桥数 - 直径。
树的桥数 = 边数。
2.2次dfs求树的直径:从任意点出发,求出距离该点最远的点pos ,再从pos出发,找到距离pos最远的点的距离ans。ans即为直径。无向图为了避免标记,传递参数时把父节点的序号标上。
3.用vector会MLE,所以需要用前向星。又因为是无向图且有重边,所以需要在前向星中加入flag。edge[i]遍历过后,反边不再遍历,即edge[i].flag = edge[i^1].flag = 0,第i条边和第i^1条边是互为反边。重边视为不同边。
#include <bits/stdc++.h>
#define N 200005
#define M 1000005
#define inf 0x3f3f3f3f
#define mod 1000000007
using namespace std;
int n , m ;
int vis[N] ;
int low[N] ;
int dfn[N] ;
int s[N] ;
int id[N] ;
int top1 ;
int lay ;
int head[N] , head1[N] ;
int cnt ;
int tcc_num ;
int pos , ans ;
int bridge_num ;
int bridge[M][2] ;
struct Edge
{
int to , next ;
bool flag ;
} edge[M << 1] ;
void Tarjin(int u)
{
int i , j , v ;
lay ++ ;
vis[u] = 1 ;
low[u] = lay ;
dfn[u] = lay ;
s[++ top1] = u ;
for(i = head[u] ; i != -1 ; i = edge[i].next)
{
if(!edge[i].flag)
continue ;
edge[i].flag = edge[i ^ 1].flag = 0 ;
v = edge[i].to ;
if(vis[v] == 0)
{
Tarjin(v) ;
if(dfn[u] < low[v])
{
bridge_num ++ ;
bridge[bridge_num][0] = u ;
bridge[bridge_num][1] = v ;
}
}
if(vis[v] == 1)
low[u] = min(low[u] , low[v]) ;
}
if(low[u] == dfn[u])
{
tcc_num ++ ;
while(s[top1] != u)
{
id[s[top1]] = tcc_num ;
vis[s[top1]] = 2 ;
top1 -- ;
}
id[s[top1]] = tcc_num ;
vis[s[top1]] = 2 ;
top1 -- ;
}
}
int dfs(int u , int fa , int d)
{
int i , j , v ;
if(d > ans)
{
ans = d ;
pos = u ;
}
for(i = head[u] ; i != -1 ; i = edge[i].next)
{
v = edge[i].to ;
if(fa == v)
continue ;
dfs(v , u , d + 1) ;
}
}
void cal()
{
int i , j ;
ans = 0 ;
dfs(1 , 1 , 0) ;
ans = 0 ;
dfs(pos , pos , 0) ;
printf("%d\n" , bridge_num - ans) ;
}
void addedge(int u , int v)
{
edge[cnt].flag = 1 ;
edge[cnt].to = id[v] ;
edge[cnt].next = head[id[u]] ;
head[id[u]] = cnt ++ ;
edge[cnt].flag = 1 ;
edge[cnt].to = id[u] ;
edge[cnt].next = head[id[v]] ;
head[id[v]] = cnt ++ ;
}
void change()
{
int i , j ;
cnt = 0 ;
memset(head , -1 , sizeof(head)) ;
for(i = 1 ; i <= bridge_num ; i ++)
addedge(bridge[i][0] , bridge[i][1]) ;
}
int main()
{
int i , j ;
int u , v ;
while(scanf("%d%d" , &n , &m) && !(n == 0 && m == 0))
{
cnt = 0 ;
memset(vis , 0 , sizeof(vis)) ;
memset(head , -1 , sizeof(head)) ;
for(i = 1 ; i <= n ; i ++)
id[i] = i ;
for(i = 1 ; i <= m ; i ++)
{
scanf("%d%d" , &u , &v) ;
addedge(u , v) ;
}
lay = top1 = 0 ;
tcc_num = bridge_num = 0 ;
Tarjin(1) ; //缩点
change() ; //新建一个树
cal() ;
}
}