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. shell_script_查询主机名、ip地址 、DNS地址

    #!/bin/bashhostnameip=`/sbin/ifconfig eth0|grep "inet addr:"|sed 's/Bcast.*$//'g |awk -F & ...

  2. uva11134 - Fabled Rooks(问题分解,贪心法)

    这道题非常好,不仅用到了把复杂问题分解为若干个熟悉的简单问题的方法,更是考察了对贪心法的理解和运用是否到位. 首先,如果直接在二维的棋盘上考虑怎么放不好弄,那么注意到x和y无关(因为两个车完全可以在同 ...

  3. Java 面向对象编程 tricks

    方法中描述了参数: 1. 构造器重载 重载构造器时,使用描述了参数的静态工厂方法名,这样做的意义何在呢?就在于为动作赋予意义,提升代码的可解释性: 传统的实例化方式: Complex fulcrumP ...

  4. PHP Smarty无法解析模板文件

    /****************************************************************************** * PHP Smarty无法解析模板文件 ...

  5. [BZOJ1022][SHOI2008]小约翰的游戏

    bzoj luogu sol 显然这个玩意儿和普通\(Nim\)游戏是有区别的. 形式化的,\(Nim\)游戏的关键在于决策集合为空者负,而这里的决策集合为空者胜. 所以就显然不能直接用\(SG\)函 ...

  6. RabbitMQ用户角色及权限控制(不错)

    ########################用户角色####################### RabbitMQ的用户角色分类:none.management.policymaker.moni ...

  7. linux下ioctl遇到的坑

    在驱动编程里面经常会用到ioctl的系统调用,发现cmd = 2的时候,用户ioctl直接返回-1. 原因在于在linux-x.xx/fs/ioctl.c定义的do_vfs_ioctl函数 int d ...

  8. ZIP 算法详解 (转!)

    zip 的压缩原理与实现(lz77 算法压缩) 无损数据压缩是一件奇妙的事情,想一想,一串任意的数据能够根据一定的规则转换成只有原来 1/2 - 1/5 长度的数据,并且能够按照相应的规则还原到原来的 ...

  9. 前端跨域问题,以及ajax,jsonp,json的区别

    看了很多网上的资料,小七感觉都没有完全解决我的疑惑以及问题,所以特意拿出通俗易懂的话讲解跨域问题,以及ajax,jsonp,json的区别.首先先说跨域问题什么时候需要跨域?[1]域名不同(即网址不同 ...

  10. loj 6485 LJJ学二项式定理 —— 单位根反演

    题目:https://loj.ac/problem/6485 先把 \( a_{i mod 4} \) 处理掉,其实就是 \( \sum\limits_{i=0}^{3} a_{i} \sum\lim ...