[dfs][树的直径] Jzoj P1737 删边

Posted comfortable

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[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 }

 

以上是关于[dfs][树的直径] Jzoj P1737 删边的主要内容,如果未能解决你的问题,请参考以下文章

[JZOJ5465]道路重建--边双缩点+树的直径

luogu3761 [TJOI2017]城市

bzoj 2785 jzoj 2755 2012东莞市选树的计数(计数+dp):

树的直径概念及求解

树的直径和LCA

求树的直径+并查集(bfs,dfs都可以)hdu4514