[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 }

原文地址:https://www.cnblogs.com/Comfortable/p/9281165.html

时间: 2024-09-29 11:07:21

[dfs][树的直径] Jzoj P1737 删边的相关文章

拓扑排序,树的直径模板(CF14D 枚举删边)

HDU4607 树的直径 #include <stdio.h> #include <string.h> #include <iostream> #include <queue> #include <vector> using namespace std; #define N 100005 #define INF 1<<30 int n,dis[N],E; bool vis[N]; vector<int>G[N]; //注意

【BZOJ-1912】patrol巡逻 树的直径 + DFS(树形DP)

1912: [Apio2010]patrol 巡逻 Time Limit: 4 Sec  Memory Limit: 64 MBSubmit: 1034  Solved: 562[Submit][Status][Discuss] Description Input 第一行包含两个整数 n, K(1 ≤ K ≤ 2).接下来 n – 1行,每行两个整数 a, b, 表示村庄a与b之间有一条道路(1 ≤ a, b ≤ n). Output 输出一个整数,表示新建了K 条道路后能达到的最小巡逻距离.

&lt;学习笔记&gt; 树的直径 Bfs、Dfs

树的直径为树上最长的一条路径(不经过重复节点),也可以看做是树上最长路. 通常的求法: 1.两边Bfs或两边Dfs 2.树形dp(端点为根和仅经过根). emmm ..蒟蒻表示目前只会第一种QAQ. 从树中找出任意一点,求出与他距离最远的点s,再用同样的方法求出与s距离最远的点t,s-t即为树的直径. Bfs代码 1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cma

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

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4514 这题主要是叫我们求出树的直径,在求树的直径之前要先判断一下有没有环 树的直径指的就是一棵树上面距离最远的两点的距离,有时也可以指最远的两点之间的路径. 至于树的直径怎么求,我们首先要知道一个结论,树上面随便取一点,离这一点最远的那个点一定是树的直径上面的两点中的一点 证明的博客:https://www.cnblogs.com/wuyiqi/archive/2012/04/08/2437424.

大臣的旅费---树的直径(dfs)

很久以前,T王国空前繁荣.为了更好地管理国家,王国修建了大量的快速路,用于连接首都和王国内的各大城市. 为节省经费,T国的大臣们经过思考,制定了一套优秀的修建方案,使得任何一个大城市都能从首都直接或者通过其他大城市间接到达. 同时,如果不重复经过大城市,从首都到达每个大城市的方案都是唯一的. J是T国重要大臣,他巡查于各大城市之间,体察民情. 所以,从一个城市马不停蹄地到另一个城市成了J最常做的事情.他有一个钱袋,用于存放往来城市间的路费. 聪明的J发现,如果不在某个城市停下来修整,在连续行进过

[xyz模拟题]动态维护树的直径

专出神题的xyz. 支持删加边.修改点权.维护树的直径. LCT 需要额外记录子树信息.用一个堆维护. #include<cstdio> #include<cstring> #include<algorithm> #include<set> #include<vector> #include<queue> using namespace std; #define rep(i,x,y) for(i=x;i<=y;i++) #def

倍增/线段树维护树的直径 hdu5993/2016icpc青岛L

题意: 给一棵树,每次询问删掉两条边,问剩下的三棵树的最大直径 点10W,询问10W,询问相互独立 Solution: 考虑线段树/倍增维护数的直径 考虑dfs序的一个区间 [l, r] 是联通的 而我们知道了有 l <= k < r, 且知道 [l, k] 和 [k + 1, r] 两个区间的直径端点及长度 假设两个区间的直径端点分别为 (l1, r1) 和 (l2, r2) 那么 [l, r] 这个区间的直径长度为 dis(l1, r1) dis(l1, l1)  dis(l1, r2)

[模拟赛10.12] 老大 (二分/树的直径/树形dp)

[模拟赛10.12] 老大 题目描述 因为 OB 今年拿下 4 块金牌,学校赞助扩建劳模办公室为劳模办公室群,为了体现 OI 的特色,办公室群被设计成了树形(n 个点 n ? 1 条边的无向连通图),由于新建的办公室太大以至于要将奖杯要分放在两个不同的地方以便同学们丢硬币进去开光,OB 想请你帮帮他看看奖杯放在哪两个办公室使得在任意一个在劳模办公室做题的小朋友能最快地找到奖杯来开光. 一句话题意:给出一个 n 个点的树,在两个合适且不同的点放上奖杯,使得每个点到最近的奖杯距离最大值最小. 输入

poj1849(求树的直径)

题目链接:http://poj.org/problem?id=1849 题意:有一颗n个结点的带权的无向树, 在s结点放两个机器人, 这两个机器人会把树的每条边都走一遍, 但是最后机器人不要求回到出发点. 问你两个机器人走的路总长之和的最小值是多少? 分析:如果从某点出发遍历完一棵树再回来,那么所有边都会走两遍,而从某点有两个机器人出发去遍历,因为不用回来,所以最后那两个人距离越远越好,可以从树的直径上某个点背道而驰,那么这段距离(树的直径)只走了一遍,其他的要走两遍,所以ans=sum*2-l