一、题目描述:

  给你一棵 $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 }

四、写题心得:

  这个题想了一整天,不过确实是一道好题。加油!拜拜!

posted on 2023-05-26 09:36  trh0630  阅读(121)  评论(0)    收藏  举报