从这题学到许多,故写题解以记之。(orz ix35)
分个类:
对上面情况的判断,就是数 \(p\) 子树内有多少 \([l,r]\) 内的节点。把子树问题转化成 dfn 就是个二维数点,由于是静态的,可以直接主席树做到 1log。
先抛个一开始假想的时候的结论:一堆点(一个区间)的 LCA 显然可以直接线段树维护,而可以证明,若按照 dfn 排序,则答案是两端的 LCA。很可以感性理解,但也容易证明,结合虚树理论想一下就出来了。但这对做这题并没有用,直接维护区间反而好写。注意到 LCA 也是可叠加的,于是也可以用 ST 表做到 \(\mathrm O(1)\) 查询,预处理由于次数是线对,所以需要用 \(\mathrm O(n\log n)-\mathrm O(1)\) 的欧拉序 + ST 表求 LCA。
将 \(l\sim r\) 按 dfn / 欧拉序排序后,将 \(p\) 也插进去,那么根据欧拉序 + ST 表求 LCA 法可知,\(p\) 与后继的 LCA 是与更后面节点的 LCA 的后代(越往后面取,LCA 深度越小);左边类似。于是我们只需要求 \(p\) 的前驱与后继。
自然想到 2log 的树套树。考虑其本质,前驱后继其实是个变了个形的二维数点,范围是第一维是对应区间,第二维是值域上的前缀 / 后缀,运算从加法变成了求 max / min。但是这玩意没有可减性,所以树套树也是只能区间查询,而非前缀查询然后相减。幸运的是,第二维(值域维)是前缀 / 后缀,可以正反两遍对该维主席树,然后对第一维区间查询。
但是这样太麻烦了(大雾)。前驱后继这玩意有更优秀的性质:每个点的点权就是第二维坐标。所以如果我们把第一维压扁的话,那么就不是一个复杂的 RMQ,而是直接找有值的地方,可以用类似树上二分的更简单的写法(但是区间查询的写法更为直观?)。而「有值」这玩意(即 cnt)是可减的,于是我们可以直接将左右两端两棵树相减,然后在上面二分。
code(写老长了)
原文地址:https://www.cnblogs.com/ycx-akioi/p/solution-p6071.html