一、题目描述:
给你一棵 $n$ 个点,$n$ 条边的无向图,边带权,保证所有点都连通。
求任意去掉一条边之后,在保持所有点依然都连通的情况下,直径的最小值。
数据范围:$3\leq n\leq 2e5$ 。
二、解题思路:
首先我们找出环,我用的是拓扑排序,便于找出环上的点。
考虑把这个图看成几棵分开的树,他们的根都在同一个环上,然后分类讨论:
$Situation\ 1:最长直径在树上$
$这个比较好求,分别统计每棵树的直径就好了,因为我们只能断掉环上的边$
$Situation\ 2:最长直径在环上$
$啊啊啊\ !\ 这种情况真的是想自闭了,调了一天才调出来$ 。
$设环的长度为 \ cc\ ,环上的点分别是\ c_1,c_2,...,c_{cc}$ 。
$pre_i\ 表示从\ 1\ 到\ i\ 的边权和,用前缀和计算$
$suf_i\ 表示从\ i\ 到\ n\ 的边权和,用前缀和计算$
$f_i\ 表示以\ c_i\ 为根的树上,以\ c_i\ 为起点的最长链的长度$
$不难发现就是求断掉一条边之后,找到环上两个点\ i,\ j,使得\ f_i+f_j+dis_{i,j}\ 最大$
$假设我们断掉了\ c_k\ 与\ c_{k-1}\ 之间的边,现在我们再分三种情况讨论:$
$Case\ 1:我们选择了两个点\ i,j,满足\ 1\leq j<i\leq k-1$
$注意式子\ f_i+f_j+dis+{i,j}=(pre_i+f_i)-(pre_j-f_j)=(f_i+pre_i)+(f_j-pre_j)$
$所以只要找出最大的\ f_i+pre_i,f_i-pre_i\ 即可,这是一个简单的序列统计$
$Case\ 2:我们选择了两个点\ i,j,满足\ k\leq i < j\leq cc$
$与\ Case\ 1\ 大同小异,可以对照一下,注意用\ suf\ 数组$
$只需要找出最大的\ f_i+suf_i,f_i-suf_i\ 即可,这也是一个简单的序列统计$
$Case\ 3:我们选择了两个点\ i,j,满足\ 1\leq i\leq k-1,k\leq j\leq cc$
$l_i\ 表示\ max(f_j+pre_j),j\in [1,i]$
$r_i\ 表示\ max(f_j+suf_j),j\in [r,cc]$
$答案即是\ l_{k-1}+r_k,这也是一个简单的序列统计$
至此,这个题终于分析完了。时间复杂度 $O(n)$
三、完整代码:
1 #include<iostream> 2 #define N 200010 3 #define ll long long 4 using namespace std; 5 ll n,U,V,W,cc,ans1,ans2; 6 ll du[N],p1[N],p2[N],v1[N],v2[N]; 7 ll c[N],f[N],q[N],a1[N],a2[N],val[N]; 8 ll l1[N],l2[N],fl[N],r1[N],r2[N],fr[N]; 9 struct EDGE{ 10 ll v,w,nxt; 11 }edge[N*2]; 12 ll head[N],cnt; 13 void add(ll u,ll v,ll w) 14 { 15 edge[++cnt].v=v;edge[cnt].w=w; 16 edge[cnt].nxt=head[u];head[u]=cnt; 17 } 18 void top_sort() 19 { 20 ll l=1,r=0; 21 for(ll i=1;i<=n;i++) 22 if(du[i]==1) 23 q[++r]=i,v1[i]=1; 24 while(l<=r) 25 { 26 ll u=q[l];l++; 27 for(ll i=head[u];i!=-1;i=edge[i].nxt) 28 { 29 ll to=edge[i].v;du[to]--; 30 if(du[to]==1&&!v1[to]) 31 v1[to]=1,q[++r]=to; 32 } 33 } 34 } 35 void dfs1(ll u,ll ff) 36 { 37 for(ll i=head[u];i!=-1;i=edge[i].nxt) 38 if(edge[i].v!=ff&&!v1[edge[i].v]) 39 { 40 v1[edge[i].v]=v2[edge[i].v]=1; 41 c[++cc]=edge[i].v,val[cc]=edge[i].w; 42 dfs1(edge[i].v,u);break; 43 } 44 } 45 void dfs2(ll s,ll u,ll ff) 46 { 47 for(ll i=head[u];i!=-1;i=edge[i].nxt) 48 { 49 ll to=edge[i].v; 50 if(v2[to])continue; 51 v2[to]=1;dfs2(s,to,u); 52 ll t=a1[to]+edge[i].w; 53 if(t>a1[u]) a2[u]=a1[u],a1[u]=t; 54 else if(t>a2[u]) a2[u]=t; 55 } 56 f[s]=a1[u],ans1=max(ans1,a1[u]+a2[u]); 57 } 58 int main() 59 { 60 ios::sync_with_stdio(false); 61 cin.tie(0);cout.tie(0); 62 cin>>n; 63 for(ll i=1;i<=n;i++) 64 head[i]=-1; 65 for(ll i=1;i<=n;i++) 66 { 67 cin>>U>>V>>W; 68 add(U,V,W),du[U]++; 69 add(V,U,W),du[V]++; 70 } 71 top_sort(); 72 for(ll i=1;i<=n;i++) 73 if(!v1[i]) 74 { 75 dfs1(i,0); 76 break; 77 } 78 for(ll i=1;i<=cc;i++) 79 dfs2(i,c[i],0); 80 for(ll i=1;i<=cc;i++) 81 p1[i]=p1[i-1]+val[i]; 82 for(ll i=cc;i>=1;i--) 83 p2[i-1]=p2[i]+val[i]; 84 l1[0]=r1[cc+1]=-1e18,ans2=1e18; 85 for(ll i=1;i<=cc;i++) 86 { 87 l1[i]=max(l1[i-1],f[i]-p1[i]); 88 l2[i]=max(l2[i-1],f[i]+p1[i]); 89 fl[i]=max(fl[i-1],f[i]+p1[i]+l1[i-1]); 90 } 91 for(ll i=cc;i>=1;i--) 92 { 93 r1[i]=max(r1[i+1],f[i]-p2[i]); 94 r2[i]=max(r2[i+1],f[i]+p2[i]); 95 fr[i]=max(fr[i+1],f[i]+p2[i]+r1[i+1]); 96 } 97 for(ll i=1;i<=cc;i++) 98 { 99 ll t1=l2[i-1]+r2[i]; 100 ll t2=fl[i-1],t3=fr[i]; 101 ans2=min(ans2,max(t1,max(t2,t3))); 102 } 103 cout<<max(ans1,ans2)<<'\n'; 104 return 0; 105 }
四、写题心得:
这个题想了一整天,不过确实是一道好题。加油!拜拜!
浙公网安备 33010602011771号