打2019ICPC上海网络赛的时候,碰到了一道题(A),可以转化成动态维护树的直径的模型。但是由于不会写,就想要学习该算法,发现方法还多,一点一点学把。
首先是例题:https://nanti.jisuanke.com/t/41398
方法一:最容易理解和暴力的方法(树剖LCA+线段树)
树剖维护树的LCA,线段树合并时维护树的直径。
思路:
1.首先要会树剖,用树剖处理该树,得到一个线段树维护的DFS序(其实就是树剖)。
2.建立一颗线段树,区间[l,r]表示包含点l,r中间的所有点对的最长直径,同时维护直径的两端点。
关键就在于维护直径和直径的两端点。就在于线段树合并。
合并的时候,采取最暴力4次比较,即:左子树会有一个直径,右子树会有一个直径,分别是四个点,分别比较四个点之间的LCA,同时获得最长的直径。O(4*long n)。
查询的时候,整棵树的直径,就是维护直径的线段树的跟所表示的(x,y)和l。
注意,这个方法复杂度较大,跑1e5的数据(1e5个点,1e5次查询)需要的时间大概是4000+ms左右,如果常数优化可能会快一点,但是估计很有限,所以如果时间在1000ms的话,估计只能处理1e4的数据量了。
(这个是没有常数优化的)
下面就是代码了,写的可能比较乱(一开始用的类,结果反而麻烦了QAQ)
但是个人感觉还是比较清楚的,两个类实现的两个功能:第一个是树剖动态维护LCA。第二个是线段树动态维护树的直径。
#include
#include
#include
#include
#include
// srand((unsigned)time(NULL));rand();
#include