zoukankan      html  css  js  c++  java
  • [dfs][树的直径] Jzoj P1737 删边

    Description

      给出N个点,N-1条边的连通图.
      现要求删除一条边,使得连通块的直径总和最大.所谓连通块的直径是指连通块中最远两点之间的距离。
         问:直径总和最大是多少?
     

    Input

      文件名为 delete.in
      第一行正整数N.
      接下来N-1行.每行两个数,A,B,LEN表示A,B(1<=A,B<=N)有一条长度为Len(1<=Len<=1000)的边连接着.

    Output

      文件名为 delete.out
      一个数Ans.直径总和的最大值.
     

    Sample Input

    10
    2 1 982
    3 1 169
    4 1 934
    5 1 325
    6 1 735
    7 1 675
    8 2 302
    9 3 450
    10 5 173
     

    Sample Output

    2668
     

    Data Constraint

     
     

    Hint

    【数据范围】
      30% N<=100
      70% N<=5000
      100% N<=100000

    题解

    • 首先,我们要预处理出每一棵子树的直径和子树中的最远点,次远点和子树中过x的最长链、次长链和次次长链
    • 那么考虑删去一条边后直径有哪几种情况
    • ①在x的子树里
    • ②在x上面的联通块的直径
    • ③x子树没被删去的最远点与x上方最远点的和
    • ④在x不同子树上最远的点的和
    • 那么考虑一下怎么求:
    • ①预处理得出
    • ②在递归时可以记录当前经过联通块的最大值
    • ③x子树里的在预处理里已经求出来,那么不在x子树里的也就是递归经过x的最长链
    • ④也就是在x子树里不含被删子树的最远点和次远点

    代码

     1 #include<cstdio>
     2 #include<iostream>
     3 using namespace std;
     4 struct edge { int to,from,v; }e[100010*2];
     5 int mx[2][100010][4],d[2][100010][4],fa[100010],head[100010],dis[100010],ans,n,cnt;
     6 void insert(int x,int y,int z) { e[++cnt].to=y; e[cnt].v=z; e[cnt].from=head[x]; head[x]=cnt; }
     7 void dfs(int x)
     8 {
     9     for (int i=head[x];i;i=e[i].from)
    10     {
    11         int v=e[i].to;
    12         if (v!=fa[x])
    13         {
    14             fa[v]=x;
    15             dfs(v);
    16             dis[x]=max(dis[x],dis[v]);
    17             if (dis[v]>mx[0][x][1])
    18             {
    19                 mx[0][x][2]=mx[0][x][1];
    20                 mx[0][x][1]=dis[v];
    21                 d[0][x][1]=v;
    22             }
    23             else if (dis[v]>mx[0][x][2]) mx[0][x][2]=dis[v];
    24             int vis=mx[1][v][1]+e[i].v;
    25             if (vis>mx[1][x][1])
    26             {
    27                 mx[1][x][3]=mx[1][x][2];
    28                 mx[1][x][2]=mx[1][x][1];
    29                 mx[1][x][1]=vis;
    30                 d[1][x][2]=d[1][x][1];
    31                 d[1][x][1]=v;
    32             }
    33             else
    34                 if (vis>mx[1][x][2])
    35                 {
    36                     mx[1][x][3]=mx[1][x][2];
    37                     mx[1][x][2]=vis;
    38                     d[1][x][2]=v;
    39                 }
    40                 else if (vis>mx[1][x][3]) mx[1][x][3]=vis;
    41         }
    42     }
    43     dis[x]=max(mx[1][x][1]+mx[1][x][2],mx[0][x][1]);
    44 }
    45 void find(int x,int a,int b)        
    46 {
    47     int mx1,mx2,mx3;
    48     if (x!=1) ans=max(ans,dis[x]+a);
    49     for (int i=head[x];i;i=e[i].from)
    50     {
    51         int v=e[i].to;
    52         if (v!=fa[x])
    53         {
    54             if (d[1][x][1]==v)
    55             {
    56                 mx1=mx[1][x][2];
    57                 mx2=mx[1][x][2]+mx[1][x][3];
    58             }
    59             else
    60             {
    61                 mx1=mx[1][x][1];
    62                 if (d[1][x][2]==v) mx2=mx[1][x][1]+mx[1][x][3]; else mx2=mx[1][x][1]+mx[1][x][2];
    63             }
    64             if (d[0][x][1]==v) mx3=mx[0][x][2]; else mx3=mx[0][x][1];
    65             find(v,max(max(a,mx3),max(mx1+b,mx2)),max(b,mx1)+e[i].v);
    66         }
    67     }
    68 }
    69 int main()
    70 {
    71     scanf("%d",&n);
    72     for (int i=1;i<=n-1;i++)
    73     {
    74         int x,y,z;
    75         scanf("%d%d%d",&x,&y,&z);
    76         insert(x,y,z); insert(y,x,z);
    77     }
    78     dfs(1);
    79     find(1,0,0);
    80     printf("%d",ans);
    81     return 0;
    82 }
  • 相关阅读:
    前端 整理的待学习技术点
    为Xamarin更好的开发而改写的库
    C#函数式编程之可选值
    C#函数式编程之序列
    C#函数式编程之标准高阶函数
    C#函数式编程之递归调用
    C#函数式编程之缓存技术
    C#函数式编程之惰性求值
    C#函数式编程之部分应用
    Xamarin.Android之动画
  • 原文地址:https://www.cnblogs.com/Comfortable/p/9281165.html
Copyright © 2011-2022 走看看