【2020.11.20提高组模拟】祖先(ancestor) 题解
【2020.11.20提高组模拟】祖先(ancestor) 题解
题目描述
对于每个\(i\),它都要往前面拜访它的祖先。对于\(i\)之前的编号为\(j\)的节点,如果要拜访的话需要满足对于\(\forall k\in (j,i],j是k的祖先\)。
\(n \le 2*10^6+2*10^5\)。
Solution
首先如果用对于每一个\(i\)向前枚举\(j,k\)再判断\(LCA\)或其他暴力等做法的话,时间复杂度\(O(n^4),O(n^3)\)不等。
考虑反过来计算每一个j对其后代的贡献。
首先显然如果有\(i\)将会在\(j\)产生贡献,\(j\)一定是\(i\)的祖先,反过来就是\(i\)在\(j\)的子树内,同时\([j,i]\)这段区间都应该在\(j\)的子树内。
如果记录\(dfn\)序之后,我们就可以把这个约束条件写成:
\[\forall i\in[j,n],dfn_j\le dfn_i\le dfn_j+size_j
\]
左右拆分,发现可以ST表+二分/倍增得到\(dfn\)的答案区间。
预处理ST表后询问都是\(O(n\log n)\)的。
Code-\(O(n\log n)\)
Std
如何不用把二分变成线性的呢?伟大的\(\texttt{Tommy0103}\)用在跑操时想到了如何像题解一样想出了如何用单调栈维护!
如果\(i\)在\(j\)的子树中
\[dfn_j\le dfn_i\le dfn_j+size_j
\]
那么\(i\)的子树也当然都在\(j\)的子树内
\[dfn_j\le dfn_i\le dfn_i+size_i\le dfn_j+size_j
\]
就可以分成两个单调栈在线性时间复杂度内维护了!