这题因为一些小细节还是\(debug\)了很久。。。不过我第一次用脚本对拍,不亏。

先随便找一个点作为根,算出答案,即所有点对到这个点的距离和的最大值,并记录所有距离最大的点对。如果这个点在任意一个距离最大的点对之间的路径上,那么答案显然不能再优了,因为这个点对的答案是不能减小了的。如果有两个距离最大的点对不在根的同一子树中,答案也是显然不能再优了的,因为一个点对答案减小的同时,另一个会增大。只有当所有距离最大的点对在根的同一子树中,这时更优答案可能在这个子树里,向这个子树递归处理就行了。为什么说可能?因为往这个子树走的同时可能会存在在另一个子树中原本不是距离最大的点对变为距离最大的点对,所以我们要一直对答案取最小值。直接走最多会走\(n\)次,而如果我们每次都走子树的重心,最多走\(logN\)次,所以总时间复杂度\(O(m\log n)\)。

现在讲讲具体怎么实现,主要就难在怎么判断根在不在两个点之间的路径上。求\(LCA\)是最简单粗暴的方法,但时间复杂度要多一个\(log\),其实类似于点分治里的统计,只需要看这个点对的两个点在不在同一子树里就行了,若在,则路径不经过根,反之亦然。

然后,看\(Code\)吧。

#include <iostream>
#include <cstdio>
#define INF 2147483647
using namespace std;
inline int read(){
int s = 0, w = 1;
char ch = getchar();
while(ch < '0' || ch > '9') { if(ch == '-') w = -1; ch = getchar(); }
while(ch >= '0' && ch <= '9') { s = s * 10 + ch - '0'; ch = getchar(); }
return s * w;
}
const int MAXN = 100010;
struct Edge{
int next, to, dis;
}e[MAXN << 1];
int head[MAXN], num, x[MAXN], y[MAXN], vis[MAXN], maxson[MAXN], p[MAXN], q[MAXN], belong[MAXN], deep[MAXN], size[MAXN];
int n, m, root, Max, ans = 2147483647;
inline void Add(int from, int to, int dis){
e[++num].to = to;
e[num].dis = dis;
e[num].next = head[from];
head[from] = num;
}
void getRoot(int u,int fa,int ALL){ //找重心
size[u] = 1; maxson[u] = 0;
for(int i = head[u]; i; i = e[i].next)
if(e[i].to != fa && !vis[e[i].to]){
getRoot(e[i].to, u, ALL);
size[u] += size[e[i].to];
maxson[u] = max(maxson[u], size[e[i].to]);
}
maxson[u] = max(maxson[u], ALL - size[u]);
if(maxson[u] < Max) root = u, Max = maxson[u];
}
void dfs(int u, int fa, int dep, int rt){ //算出每个点的深度,并标记属于根的哪棵子树
belong[u] = rt;
deep[u] = dep;
for(int i = head[u]; i; i = e[i].next)
if(e[i].to != fa)
dfs(e[i].to, u, dep + e[i].dis, rt);
}
void Solve(int u){
if(vis[u]){
printf("%d\n", ans);
exit(0);
}
vis[u] = 1;
for(int i = head[u]; i; i = e[i].next)
dfs(e[i].to, u, e[i].dis, e[i].to);
deep[u] = 0;
Max = 0; p[0] = 0;
int last = 0;
for(int i = 1; i <= m; ++i) //找出所有距离最大的点对并求出答案
if(deep[x[i]] + deep[y[i]] > Max)
Max = deep[x[i]] + deep[y[i]], p[p[0] = 1] = i;
else if(deep[x[i]] + deep[y[i]] == Max)
p[++p[0]] = i;
ans = min(ans, Max); //更新答案
for(int i = 1; i <= p[0]; ++i){
if(belong[x[p[i]]] != belong[y[p[i]]]){ //如果有一个点对之间的路径经过根,当前答案一定是最优的
printf("%d\n", ans);
exit(0);
}
else
if(!last) last = belong[x[p[i]]];
else if(last != belong[x[p[i]]]){ //如果两个点对不在同一子树里,当前答案也一定最优
printf("%d\n", ans);
exit(0);
}
}
Max = 9996666;
getRoot(last, u, size[last]); //找子树的重心
Solve(root); //递归处理
}
int a, b, c;
int main(){
n = read(); m = read();
for(int i = 2; i <= n; ++i){
a = read(); b = read(); c = read();
Add(a, b, c); Add(b, a, c);
}
for(int i = 1; i <= m; ++i){
x[i] = read(); y[i] = read();
}
Max = 9996666; getRoot(1, 0, n);
Solve(root);
return 0;
}

【洛谷 P4886】 快递员 (点分治)的更多相关文章

  1. [洛谷P4886]快递员

    题目大意:一个$n$个点的树,树上有$m$个点对$(a,b)$,找到一个点$x$,使得$max(dis(x,a_i)+dis(x,b_i))$最小 如果做过幻想乡的战略游戏这道题,应该这道题的思路一眼 ...

  2. 洛谷P3810 陌上花开 CDQ分治(三维偏序)

    好,这是一道三维偏序的模板题 当然没那么简单..... 首先谴责洛谷一下:可怜的陌上花开的题面被无情的消灭了: 这么好听的名字#(滑稽) 那么我们看了题面后就发现:这就是一个三维偏序.只不过ans不加 ...

  3. [洛谷P3806] [模板] 点分治1

    洛谷 P3806 传送门 这个点分治都不用减掉子树里的了,直接搞就行了. 注意第63行 if(qu[k]>=buf[j]) 不能不写,也不能写成>. 因为这个WA了半天...... 如果m ...

  4. 洛谷P4390 Mokia CDQ分治

    喜闻乐见的CDQ分治被我搞的又WA又T..... 大致思路是这样的:把询问用二维前缀和的思想拆成4个子询问.然后施CDQ大法即可. 我却灵光一闪:树状数组是可以求区间和的,那么我们只拆成两个子询问不就 ...

  5. 洛谷P4178 Tree (点分治)

    题目描述 给你一棵TREE,以及这棵树上边的距离.问有多少对点它们两者间的距离小于等于K 输入输出格式 输入格式:   N(n<=40000) 接下来n-1行边描述管道,按照题目中写的输入 接下 ...

  6. 洛谷 4178 Tree——点分治

    题目:https://www.luogu.org/problemnew/show/P4178 点分治.如果把每次的 dis 和 K-dis 都离散化,用树状数组找,是O(n*logn*logn),会T ...

  7. 洛谷T44252 线索_分治线段树_思维题

    分治线段树,其实就是将标记永久化,到最后再统一下传所有标记. 至于先后顺序,可以给每个节点开一个时间戳. 一般地,分治线段树用于离线,只查询一次答案的题目. 本题中,标记要被下传 222 次. Cod ...

  8. 洛谷 P4149 [IOI2011]Race-树分治(点分治,不容斥版)+读入挂-树上求一条路径,权值和等于 K,且边的数量最小

    P4149 [IOI2011]Race 题目描述 给一棵树,每条边有权.求一条简单路径,权值和等于 KK,且边的数量最小. 输入格式 第一行包含两个整数 n, Kn,K. 接下来 n - 1n−1 行 ...

  9. 洛谷 P3806 (点分治)

    题目:https://www.luogu.org/problem/P3806 题意:一棵树,下面有q个询问,问是否有距离为k的点对 思路:牵扯到树上路径的题都是一般都是点分治,我们可以算出所有的路径长 ...

随机推荐

  1. js鼠标事件相关知识

    1.mousedown->mouseup依次触发后相当于click事件 2.除了mouseenter和mouseleave外,其它的鼠标事件都是冒泡的 3.mouseover和mouseout事 ...

  2. Qt Creater 制作汽车仪表盘

    最近项目用到了模拟仪表,网上下载大神编写的按个仪表Meter没有成功 转战 QWt 编译后,在creater中仍然无法使用,只可以在代码中使用 百度说是我编译的版本不对 扔到 开始做自己的 这个用到了 ...

  3. Windows系统的高效使用

    1-WIndows10系统的入门使用 2-如何把系统盘的用户文件转移到其他盘 3-Windows装机软件一般有哪些? 4-Windows系统有哪些比较好用的下载器? 5-Windows系统中的播放器 ...

  4. C 计算金额

    #include <stdio.h> int main(int argc, char **argv) { \\定义两个变量 a金额 z跟票面 int a=0; int z=0;\\ 输入金 ...

  5. 12-Mysql数据库----多表查询

    本节重点: 多表连接查询 符合条件连接查询 子查询 准备工作:准备两张表,部门表(department).员工表(employee) create table department( id int, ...

  6. 学习bash——环境配置

    一.环境配置文件的重要性 Bash在启动时直接读取这些配置文件,以规划好bash的操作环境. 即使注销bash,我们的设置仍然保存. 二.login shell 通过完整的登录流程取得的bash,称为 ...

  7. oracle server端字符集修改

    1.oracle server端字符集查询 复制代码代码如下: select userenv('language') from dual; server字符集修改: 将数据库启动到RESTRICTED ...

  8. C语言数组作业总结

    数组作业总结 评分注意事项. 注意用Markdown语法排版,尤其注意伪代码用代码符号渲染.用符号 ``` 生成代码块. 变量名不规范,没注释,没缩进,括号不对齐,倒扣5分. PTA上写的所有代码务必 ...

  9. lintcode-128-哈希函数

    128-哈希函数 在数据结构中,哈希函数是用来将一个字符串(或任何其他类型)转化为小于哈希表大小且大于等于零的整数.一个好的哈希函数可以尽可能少地产生冲突.一种广泛使用的哈希函数算法是使用数值33,假 ...

  10. poi解析excel出现格式不正确

    后缀为xlsx的excel做系统导入时出现bug: Strict OOXML isn't currently supported, please see bug #57699 为了同时兼容03.07及 ...