Luogu 3761 [TJOI2017]城市
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]城市的更多相关文章
- luogu P3761 [TJOI2017]城市 树的直径 bfs
LINK:城市 谢邀,学弟说的一道毒瘤题. 没有真正的省选题目毒瘤 或者说 写O(n)的做法确实毒瘤. 这里给一个花20min就写完的非常好写的暴力. 容易想到枚举哪条边删掉 删掉之后考虑在哪两个点上 ...
- 【BZOJ4890】[TJOI2017]城市(动态规划)
[BZOJ4890][TJOI2017]城市(动态规划) 题面 BZOJ 洛谷 题解 数据范围都这样了,显然可以暴力枚举断开哪条边. 然后求出两侧直径,暴力在直径上面找到一个点,使得其距离直径两端点的 ...
- [洛谷P3761] [TJOI2017]城市
洛谷题目链接:[TJOI2017]城市 题目描述 从加里敦大学城市规划专业毕业的小明来到了一个地区城市规划局工作.这个地区一共有ri座城市,<-1条高速公路,保证了任意两运城市之间都可以通过高速 ...
- 换根DP+树的直径【洛谷P3761】 [TJOI2017]城市
P3761 [TJOI2017]城市 题目描述 从加里敦大学城市规划专业毕业的小明来到了一个地区城市规划局工作.这个地区一共有ri座城市,<-1条高速公路,保证了任意两运城市之间都可以通过高速公 ...
- bzoj4890[Tjoi2017]城市(树的半径)
4890: [Tjoi2017]城市 Time Limit: 30 Sec Memory Limit: 128 MBSubmit: 149 Solved: 91[Submit][Status][D ...
- [TJOI2017]城市(树的直径)
[TJOI2017]城市 题目描述 从加里敦大学城市规划专业毕业的小明来到了一个地区城市规划局工作.这个地区一共有ri座城市,<-1条高速公路,保证了任意两运城市之间都可以通过高速公路相互可达, ...
- BZOJ4890 & 洛谷3761:[TJOI2017]城市——题解
https://www.lydsy.com/JudgeOnline/problem.php?id=4890 https://www.luogu.org/problemnew/show/P3761 从加 ...
- [TJOI2017]城市 【树的直径+暴力+优化】
Online Judge:Luogu P3761 Label:树的直径,暴力 题目描述 从加里敦大学城市规划专业毕业的小明来到了一个地区城市规划局工作.这个地区一共有n座城市,n-1条高速公路,保证了 ...
- Luogu 3758 [TJOI2017]可乐(有向图邻接矩阵幂的意义 矩阵快速幂)
题目描述 加里敦星球的人们特别喜欢喝可乐.因而,他们的敌对星球研发出了一个可乐机器人,并且放在了加里敦星球的1号城市上.这个可乐机器人有三种行为: 停在原地,去下一个相邻的城市,自爆.它每一秒都会随机 ...
随机推荐
- php实现word在线浏览功能。
http://laoniangke.com/php/2012/10/08/php-doc-webview.html
- input 标签中的checkd 添加与取消
//获取是否选中 var isChecked = $('#cb').prop('checked'); //或 var isChecked = $('#cb').is(":checked&qu ...
- Bender Problem
Robot Bender decided to make Fray a birthday present. He drove n nails and numbered them from 1 to n ...
- AngularJs出现错误Error: [ng:areq]
1.没有对应的控制器 2.有控制器但是路径没有配对
- Ubuntu下部署GitLab-——基于14.04系统
搭建GitLab的目的: 方便公司开发管理代码 GitLab实现的功能: 1.关闭了gitlab的注册功能 2.修改了默认端口 3.汉化 0.前期准备 # 环境 Ubuntu 14.04 root@i ...
- 蓝桥杯 历届试题 PREV-34 矩阵翻硬币
历届试题 矩阵翻硬币 时间限制:1.0s 内存限制:256.0MB 问题描述 小明先把硬币摆成了一个 n 行 m 列的矩阵. 随后,小明对每一个硬币分别进行一次 Q 操作. 对第x行第y列的硬 ...
- hihoCoder#1050(树中最长路)
时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 上回说到,小Ho得到了一棵二叉树玩具,这个玩具是由小球和木棍连接起来的,而在拆拼它的过程中,小Ho发现他不仅仅可以拼凑成一 ...
- 第十四届华中科技大学程序设计竞赛决赛同步赛 A - Beauty of Trees
A - Beauty of Trees 题意: 链接:https://www.nowcoder.com/acm/contest/119/A来源:牛客网 Beauty of Trees 时间限制:C/C ...
- 2017-09-17 python 学习笔记
Mock 库 介绍: http://blog.csdn.net/chdhust/article/details/50663729 说明mock能做什么. 可以考虑在调试方法时使用 Mock 库
- 我和domino不得不说的故事(连载2016-3-2)
1.关于NotesViewEntry 注意:通过NotesViewEntry获取某列的值时,若该列的值为@IsExpandable or @DocNumber 或者是常量时,将不会显示. Set en ...