再来点boss
给定一个 n 个点 m 条边的无向图,保证图连通。找到两个点 s,t ,使得 s 到 t 必须经过的边最多(一条边无论走哪条路线这一条边都经过,这条边就是必须经过的边)。
首先我们要知道知道同一个边双连通分量中,任意两点之间存在至少两条无重边的简单路径。
我们可以发现同一个边双内的点之间没有必须经过的边。所以在这一道题目中,我们只需要把给我们的图给缩点缩成一棵树,然后跑两边 dfs 求树的直径即可,也就是 2 个板子套一下就可以了。
#include<bits/stdc++.h> using namespace std; const int N=1200100; int n,m,idx=1,cnt,in1,in2,num,p,q; int to[N],nxt[N],head[N],rhead[N]; int dfn[N],low[N],bridge[N],rid[N]; int dis[N]; void add(int h[],int u,int v) { idx++; to[idx]=v; nxt[idx]=h[u]; h[u]=idx; } void tarjan(int s,int last)//缩点板子,不必多说 { dfn[s]=low[s]=++cnt; for(int i=head[s];i;i=nxt[i]) { int v=to[i]; if(i==(last^1)) continue; if(!dfn[v]) { tarjan(v,i); low[s]=min(low[s],low[v]); if(dfn[s]<=low[v])//直接就是一座桥 { bridge[i]=bridge[i^1]=1; } } else low[s]=min(low[s],dfn[v]); } } void dfs_0(int s)//跑2遍dfs,求树的直径 { rid[s]=num; for(int i=head[s];i;i=nxt[i]) { int v=to[i]; if(bridge[i]||rid[v]) continue; dfs_0(v); } } void dfs_1(int s,int fa,int &u) { for(int i=rhead[s];i;i=nxt[i]) { int v=to[i]; if(v==fa) continue; dis[v]=dis[s]+1; if(dis[0]<dis[v]) { dis[0]=dis[v]; u=v; } dfs_1(v,s,u); } } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { scanf("%d%d",&in1,&in2); add(head,in1,in2); add(head,in2,in1); } tarjan(1,-1); for(int i=1;i<=n;i++) { if(!rid[i]) { num++; dfs_0(i); } } for(int s=1;s<=n;s++) { for(int i=head[s];i;i=nxt[i]) { if(rid[s]!=rid[to[i]]) { add(rhead,rid[s],rid[to[i]]); } } } dis[1]=0; dfs_1(1,0,p); dis[0]=dis[p]=0; dfs_1(p,0,q); cout<<dis[0]<<endl; return 0; }