HDU4612
题意:加一条边最多还有几座桥。
题解:
- Tarjan缩点后,任意两点之间的边都是桥。我们只要求任意两点之间最多的桥,也就是树的直径。两次BFS即可。
- 从任意点出发BFS找最远的点,再从最远的那个点BFS找最远的点便是直径。
- https://blog.csdn.net/forever_dreams/article/details/81051578树的直径可以参考这一篇,证明很详细。
代码:
#include <bits/stdc++.h>
using namespace std;
int const N = 200000 + 10;
int const M = 2000000 + 10;
int first[N],ne[M],to[M];
int n,m,tot,bridge,cnt,scc,last;
int sccno[N],dfn[N],lowlink[N];
int dis[N];
stack<int>st;
vector<int>G[N];
void add(int u,int v){
ne[++tot] = first[u];
to[tot] = v;
first[u] = tot;
}
bool Init(){
memset(first,0,sizeof(first));
tot = 0;
scanf("%d%d",&n,&m);
if(!n && !m) return false;
for(int i=0;i<m;i++){
int u,v;
scanf("%d%d",&u,&v);
add(u,v); add(v,u);
}
return true;
}
void dfs(int u,int fa){
dfn[u] = lowlink[u] = ++cnt;
st.push(u);
int num;
for(int i=first[u];i;i=ne[i]){
int v = to[i];
if(v == fa){
num++;
if(num == 1) continue; //因为无向图有反向边,所以第一次搜到的不是重边。
}
if(!dfn[v]){
dfs(v,u);
lowlink[u] = min(lowlink[v],lowlink[u]);
if(lowlink[v] > dfn[u]) bridge++;
}else if(!sccno[v]){
lowlink[u] = min(lowlink[u],dfn[v]);
}
}
if(dfn[u] == lowlink[u]){
scc++;
while(1){
int x = st.top(); st.pop();
sccno[x]= scc;
if(x == u) break;
}
}
}
void Tarjan(){
scc = cnt = bridge = 0;
memset(dfn,0,sizeof(dfn));
memset(sccno,0,sizeof(sccno));
dfs(1,0);
for(int i=0;i<N;i++) G[i].clear();
for(int i=1;i<=n;i++){
for(int j=first[i];j;j=ne[j]){
int v = to[j];
if(sccno[i] != sccno[v])
G[sccno[i]].push_back(sccno[v]), G[sccno[v]].push_back(sccno[i]);
}
}
}
int BFS(int u){
int longdist = 0;
memset(dis,-1,sizeof(dis));
queue<int>q;
q.push(u);
dis[u] = 0;
while(!q.empty()){
int p = q.front(); q.pop();
for(int i=0;i<G[p].size();i++){
int v = G[p][i];
if(dis[v] == -1){
dis[v] = dis[p] + 1;
if(dis[v] > longdist) longdist = dis[v], last = v;
q.push(v);
}
}
}
return longdist;
}
int main(){
while(Init()){
Tarjan();
BFS(1);
printf("%d\n",bridge - BFS(last));
}
}