原題連結
題解
1.已知如果兩個點之間有兩條邊不重合的路徑,那麼這兩個點就在一個邊強連通分量裡,所以我們可以把處於同一個邊強連通分量的點縮起來
在這裡,我忘記了怎麼求邊強連通分量,所以我再提醒一下自己
已知樹結構是不存在強連通分量的,它的特性是深度大的節點只有一條回到深度小的節點的邊,所以我們深度搜尋,對遇到的節點進行深搜順序標記,記錄每個節點能去的深序最小節點
假如遍歷到當前節點 \(u\),已知其子節點能去的最小的深序節點比它還小,那說明它不是這個邊強連通分量的深序最小點;
如果其子節點的最小深序節點等於它,那麼代表它是這個邊強連通分量的深序最小點
2.把邊強連通分量縮點之後,由於保證聯通的無向圖,所以剩餘節點一定是樹
樹中求兩點最大距離等價於求樹的直徑,在這裡我又忘記怎麼求了,所以我再提醒一下自己
求樹的直徑:雙dfs法,兩次搜尋深度最大的點
這裡我證明為什麼隨便找一個點然後深搜深度最大的點一定是直徑的端點
反證法:
code
// LUOGU_RID: 159070334
#include<bits/stdc++.h>
using namespace std;
int vis[300005]={0},lst[300005]={0};
int cnt=0;
stack<int> st;
vector<int> G[300005];
int belong[300005];
int tot=0;
void dfs(int now,int fa)
{
vis[now]=++cnt;
lst[now]=cnt;
st.push(now);
for(auto next:G[now])
{
if(next==fa) continue;
if(vis[next]) lst[now]=min(vis[next],lst[now]);
else
{
dfs(next,now);
lst[now]=min(lst[now],lst[next]);
}
}
if(vis[now]==lst[now])
{
int x;
tot++;
do
{
x=st.top();
st.pop();
belong[x]=tot;
}while(x!=now);
}
}
vector<int> E[300005];
int depth[300006]={0};
int root,maxd=-1;
void dfs1(int now,int fa)
{
if(depth[now]>maxd)
{
maxd=depth[now];
root=now;
}
for(auto next:E[now])
{
if(next==fa||depth[next]) continue;
depth[next]=depth[now]+1;
dfs1(next,now);
}
}
int dfs2(int now,int fa)
{
int ans=depth[now];
for(auto next:E[now])
{
if(next==fa||depth[next]) continue;//由於建邊的時候重複建邊了
depth[next]=depth[now]+1;
ans=max(ans,dfs2(next,now));
}
return ans;
}
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int x,y;
cin>>x>>y;
G[x].push_back(y);
G[y].push_back(x);
}
dfs(1,1);
for(int i=1;i<=n;i++)
{
for(auto next:G[i])
{
if(belong[next]!=belong[i])
{
E[belong[i]].push_back(belong[next]);
E[belong[next]].push_back(belong[i]);
}
}
}
dfs1(1,-1);
memset(depth,0,sizeof depth);
cout<<dfs2(root,-1);
return 0;
}