BZOJ 4890。

在树上断开一条边之后会形成两个联通块,如果要使这一条边接回去之后保持一棵树的形态,那么必须在两个联通块之间各找一个点连接。

那么,对于每一条可能断开的边,它产生的答案是以下两者的最大值:

  1、其中一个联通块的直径

  2、两个联通块中从一个点出发最短的最长距离之和再加上这一条边的权值。

这么拗口,就叫做半径好了。

断开的边是肯定要枚举的,这题的主要矛盾变成了如何求这个半径。

思考一个点的半径会产生于哪里,有以下两种来源:

  1、它子树中的点到它距离的最大值。

  2、除了它的子树中的点,其他所有结点到它父亲结点的距离的最大值加上它到它父亲结点的距离。

其实再细分下去,这个“其他所有结点到它父亲结点的距离的最大值”又有两种来源:

  1、它爷爷的最大值加上它爷爷到它父亲的距离

  2、它父亲到它的某个子树中的点的最大值。

考虑到第二条有可能包含了它,所以搜的时候需要维护三个值:它子树中的点到它距离的最大值$fir_x$,以及它子树中的点到它距离的次大值$sec_x$,以及它去到子树最大值的儿子$maxp_x$。

要注意选择维护$maxp_x$是考虑到含有多条相同链长的情况。

假设$f_{fa}$是父亲到$x$距离的最大值。

我们在往下走的时候,如果$x == maxp_x$那么用$max(f_{fa}, sec_x) + dis(x, son)$更新$f_x$,否则用$max(f_{fa}, fir_x) + dis(x, son)$来更新$f_x$。

每搜到一个点,用$max(fir_x, f_{fa})$更新答案。

其实直径就是$max(fir_x + sec_x)$,搜的时候顺便算一下就好了。

然后还有个优化,就是只有当原来这棵树上的直径的边删掉重构之后才有可能影响答案,因为如果拿出一条直径以外的边重构,直径仍然存在。这样子就可以缩减一些外层循环的计算量。(似乎有整棵树是一条链的极端数据)。

时间复杂度$O(n^2)$,常数迷人。

Code:

#include <cstdio>
#include <cstring>
using namespace std; const int N = ;
const int inf = << ; int n, tot = , head[N], dis[N], maxp[N];
int res, len = , toEdge[N], dia[N], fir[N], sec[N]; struct Edge {
int to, nxt, val;
bool lock;
} e[N << ]; inline void add(int from, int to, int val) {
e[++tot].to = to;
e[tot].val = val;
e[tot].lock = ;
e[tot].nxt = head[from];
head[from] = tot;
} inline void read(int &X) {
X = ; char ch = ; int op = ;
for(; ch > '' || ch < ''; ch = getchar())
if(ch == '-') op = -;
for(; ch >= '' && ch <= ''; ch = getchar())
X = (X << ) + (X << ) + ch - ;
X *= op;
} void dfs(int x, int fat, int nowDis, int inEdge) {
dis[x] = nowDis, toEdge[x] = inEdge;
for(int i = head[x]; i; i = e[i].nxt) {
int y = e[i].to;
if(y == fat) continue;
dfs(y, x, nowDis + e[i].val, i);
}
} inline void chkMax(int &x, int y) {
if(y > x) x = y;
} inline void chkMin(int &x, int y) {
if(y < x) x = y;
} inline int max(int x, int y) {
return x > y ? x : y;
} void dp(int x, int fat) {
for(int i = head[x]; i; i = e[i].nxt) {
int y = e[i].to;
if(y == fat || e[i].lock) continue;
dp(y, x);
if(fir[y] + e[i].val > fir[x])
sec[x] = fir[x], fir[x] = fir[y] + e[i].val, maxp[x] = y;
else if(fir[y] + e[i].val > sec[x])
sec[x] = fir[y] + e[i].val;
}
chkMax(res, fir[x] + sec[x]);
} void dp2(int x, int fat, int f) {
chkMin(res, max(f, fir[x]));
for(int i = head[x]; i; i = e[i].nxt) {
int y = e[i].to;
if(y == fat || e[i].lock) continue;
if(y == maxp[x]) dp2(y, x, max(sec[x] + e[i].val, f + e[i].val));
else dp2(y, x, max(fir[x] + e[i].val, f + e[i].val));
}
} inline int getD(int rt) {
res = ;
dp(rt, );
return res;
} inline int getR(int rt) {
res = inf;
dp2(rt, , );
return res;
} int main() {
// freopen("4.in", "r", stdin); read(n);
for(int x, y, v, i = ; i < n; i++) {
read(x), read(y), read(v);
add(x, y, v), add(y, x, v);
} dfs(, , , );
int root = ; dis[root] = ;
for(int i = ; i <= n; i++)
if(dis[i] > dis[root]) root = i;
dfs(root, , , ); int pnt = ;
for(int i = ; i <= n; i++)
if(dis[i] > dis[pnt]) pnt = i; for(int i = pnt; i != root; i = e[toEdge[i] ^ ].to)
dia[++len] = toEdge[i]; int ans = dis[pnt];
for(int i = ; i <= len; i++) {
int now = ;
e[dia[i]].lock = e[dia[i] ^ ].lock = ; memset(fir, , sizeof(fir));
memset(sec, , sizeof(sec));
memset(maxp, , sizeof(maxp)); chkMax(now, getD(e[dia[i]].to)), chkMax(now, getD(e[dia[i] ^ ].to));
chkMax(now, getR(e[dia[i]].to) + getR(e[dia[i] ^ ].to) + e[dia[i]].val); e[dia[i]].lock = e[dia[i] ^ ].lock = ;
chkMin(ans, now);
} printf("%d\n", ans);
return ;
}

Luogu 3761 [TJOI2017]城市的更多相关文章

  1. luogu P3761 [TJOI2017]城市 树的直径 bfs

    LINK:城市 谢邀,学弟说的一道毒瘤题. 没有真正的省选题目毒瘤 或者说 写O(n)的做法确实毒瘤. 这里给一个花20min就写完的非常好写的暴力. 容易想到枚举哪条边删掉 删掉之后考虑在哪两个点上 ...

  2. 【BZOJ4890】[TJOI2017]城市(动态规划)

    [BZOJ4890][TJOI2017]城市(动态规划) 题面 BZOJ 洛谷 题解 数据范围都这样了,显然可以暴力枚举断开哪条边. 然后求出两侧直径,暴力在直径上面找到一个点,使得其距离直径两端点的 ...

  3. [洛谷P3761] [TJOI2017]城市

    洛谷题目链接:[TJOI2017]城市 题目描述 从加里敦大学城市规划专业毕业的小明来到了一个地区城市规划局工作.这个地区一共有ri座城市,<-1条高速公路,保证了任意两运城市之间都可以通过高速 ...

  4. 换根DP+树的直径【洛谷P3761】 [TJOI2017]城市

    P3761 [TJOI2017]城市 题目描述 从加里敦大学城市规划专业毕业的小明来到了一个地区城市规划局工作.这个地区一共有ri座城市,<-1条高速公路,保证了任意两运城市之间都可以通过高速公 ...

  5. bzoj4890[Tjoi2017]城市(树的半径)

    4890: [Tjoi2017]城市 Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 149  Solved: 91[Submit][Status][D ...

  6. [TJOI2017]城市(树的直径)

    [TJOI2017]城市 题目描述 从加里敦大学城市规划专业毕业的小明来到了一个地区城市规划局工作.这个地区一共有ri座城市,<-1条高速公路,保证了任意两运城市之间都可以通过高速公路相互可达, ...

  7. BZOJ4890 & 洛谷3761:[TJOI2017]城市——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=4890 https://www.luogu.org/problemnew/show/P3761 从加 ...

  8. [TJOI2017]城市 【树的直径+暴力+优化】

    Online Judge:Luogu P3761 Label:树的直径,暴力 题目描述 从加里敦大学城市规划专业毕业的小明来到了一个地区城市规划局工作.这个地区一共有n座城市,n-1条高速公路,保证了 ...

  9. Luogu 3758 [TJOI2017]可乐(有向图邻接矩阵幂的意义 矩阵快速幂)

    题目描述 加里敦星球的人们特别喜欢喝可乐.因而,他们的敌对星球研发出了一个可乐机器人,并且放在了加里敦星球的1号城市上.这个可乐机器人有三种行为: 停在原地,去下一个相邻的城市,自爆.它每一秒都会随机 ...

随机推荐

  1. uva1152 - 4 Values whose Sum is 0(枚举,中途相遇法)

    用中途相遇法的思想来解题.分别枚举两边,和直接暴力枚举四个数组比可以降低时间复杂度. 这里用到一个很实用的技巧: 求长度为n的有序数组a中的数k的个数num? num=upper_bound(a,a+ ...

  2. jenkins 参数化构建和增加环境变量

    1.参数化构建 2.增加环境变量 prepare an environment for the run,需要安装Environment Injector插件

  3. PHPExcel如何把该列的值设置为文本无科学计数?

                    $obj_sheet->setCellValueExplicit($cells[$_counter].$i, (isset($val[$_value_key]) ...

  4. 通过maven的jar包库找到对应的jar包。

    查找连接: http://search.maven.org/ 查找实例 http://search.maven.org/#search|ga|1|a%3A%22log4j%22

  5. Mongodb 副本集的节点详细操作

    副本集操作 官方文档:https://docs.mongodb.com/v3.2/reference/method/js-replication/ 1 rs.add(){ _id: <int&g ...

  6. OpenCV 视频监控(Video Surveilance)的算法体系

    如前面说到的,OpenCV VS提供了6组算法的接口,分别是:前景检测.新目标检测.目标跟踪.轨迹生成.跟踪后处理.轨迹分析,除了轨迹生成用于轨迹数据的保存以外,其他5个部分都是标准的视频监控算法体系 ...

  7. web基础(五)Jquery

    jQuery是一个快速的,简洁的javaScript库(占空间特别小,但功能强大!兼容性极强!),这个库里封装了一些方法,使用户能更方便地处理HTMLdocuments.events.实现动画效果,并 ...

  8. python读取配置文件 ConfigParser

    Python 标准库的 ConfigParser 模块提供一套 API 来读取和操作配置文件. 配置文件的格式 a) 配置文件中包含一个或多个 section, 每个 section 有自己的 opt ...

  9. jdbcTemplate学习(一)

    概述 Spring JDBC抽象框架core包提供了JDBC模板类,其中JdbcTemplate是core包的核心类,所以其他模板类都是基于它封装完成的,JDBC模板类是第一种工作模式. JdbcTe ...

  10. SpringMVC + AJAX 实现多文件异步上传

    转自:https://www.jianshu.com/p/f3987f0f471f 今天,我就这个问题来写一篇如何用 SpringMVC + AJAX 实现的多文件异步上传功能.基本的代码还是沿用上篇 ...