这题因为一些小细节还是\(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. Struts2—整合Spring

    Struts2—整合Spring Spring框架是一个非常优秀的轻量级java EE容器,大部分javaEE应用,都会考虑使用Spring容器来管理应用中的组件. Struts2是一个MVC框架,是 ...

  2. 走进Android系统

    一.Android背景 [Android定义] Android是Google公司在2007年11月5日公布的基于Linux平台的开源手机操作系统. [发展历程] 2005年,Google收购企业And ...

  3. AtomicIntegerFieldUpdater使用

    假设现在有这样的一个场景: 一百个线程同时对一个int对象进行修改,要求只能有一个线程可以修改. 看看下面程序是否正确: private static int a = 100; private sta ...

  4. Linux yum安装MySQL5.7,及远程连接mysql(亲测有效!)

    一.安装配置MySQL的yum源 # 安装MySQL的yum源,下面是RHEL6系列的下载地址 rpm -Uvh http://dev.mysql.com/get/mysql-community-re ...

  5. lintcode-104-合并k个排序链表

    104-合并k个排序链表 合并k个排序链表,并且返回合并后的排序链表.尝试分析和描述其复杂度. 样例 给出3个排序链表[2->4->null,null,-1->null],返回 -1 ...

  6. js调用本地office打开服务器的office文件预览

    本来是想做成直接在网页上在线预览office文件的,但是找了好多,要不是收费,要不就是要调用别人的API不安全,所以纠结了好久还是用调用本地的office预览office文件. 废话不多说,那么怎么调 ...

  7. 使用emit发出信号

    1. 信号声明 在发送信号的模块类头文件中声明信号函数 signals: void sendRate(QString rate); 2. 在发送模块的成员函数中发出信号 emit sendRate(u ...

  8. java解析XML的方法

    1.DOM 实现方法 xml文件 <?xml version="1.0" encoding="utf-8"?> <Accounts> & ...

  9. servletContext的定义

  10. [bzoj4889] [Tjoi2017]不勤劳的图书管理员

    Description 加里敦大学有个帝国图书馆,小豆是图书馆阅览室的一个书籍管理员.他的任务是把书排成有序的,所以无序的书让他产生厌烦,两本乱序的书会让小豆产生这两本书页数的和的厌烦度.现在有n本被 ...