Task
一棵n节点树,m个询问求(a,b)路径距离。改动一条边距为0,求m个询问中最大距离的最小值。
Solution
方法一:
暴力出奇迹。
根据题意“改动一条边”,最朴素的做法是O(n)枚举改动哪一条边,该边只对路径中存在该边的询问有影响。询问可以按照是否包含该边分为两类,ans=max(包含的最大距离-v,不包含的最大距离)。
问题转化成:判定边K是否存在于路径
如果把a,b用线相连,就会构成一个环,假设边K在路径A,B上,那么一定在这个环上。即边K的两个端点都在lca的子树里,K的两个端点是a,b中某一个的祖先。判定子树借助dfs序。
怎么提高程序的效率,多骗点分呢?因为答案必定是由较大的决定的,询问按照原路径从大到小排序,如果已经有i的答案大于i+1的答案,就不必要在接下去查找了,直接break。
方法二:
树链剖分+线段树
根据表达式: ans=max(包含的最大距离-v,不包含的最大距离)。
贪心包含该边的询问都是距离大的。
如果按照原路径距离从大到小排序,必定存在一个分界位置i,[1,i]包含边v,i+1不包含边v。那么询问i+1的原距离就是“不包含的最大距离”,包含的最大距离一定是询问1.
我们可以枚举这个分界点i。如果分界点确定后,边K必定在[1,i]路径的交集上,且是交集中的边权最大值。
如何找到路径的交集呢?
当加入一条路径时,该路径上的每一条边次数+1。如果边K在路径的交集上,那么边K的次数为i。
我们需要一个数据结构帮助我们实现区间更新,区间询问:
① 给一条路径的权值+1
② 询问所有边中次数最大条件下,边权最大值。
在序列上用线段树,在树上用树链剖分。
方法三:
二分+差分
求“最大距离最小”边界情况的问题,可以用二分。
因为边长最大为1000,因此答案只可能在[mx−1000,mx]之间。Mx表示原询问路径的最大值。这样做可以提高运行效率,减少二分次数。
怎么去写check函数呢?
判定答案<=res是否可行:所有询问>res的路径都必须更改某一条边,最终答案存在于这些边的交集中,且要求权值最大。
求路径的交集等价于将路径刷漆,求次数最大的边中权值最大的边。
因为询问离线,可以进行树上的差分。
结论:x到父亲的边权值=x子树权值和。
求解x子树的权值和方法有:dfs序+BIT,树形dp,dfs序+后缀和。
在这3种方法中,第3种常数最小,也很方便。