这里给出一种树剖+倍增的方法
给出的容器序列记为(P)
首先我们对树进行重链剖分,每个点得到了一个新的编号
对每一个宝石种类开一个(vector)
然后我们把每个点的树剖编号装入其自身宝石对应的(vector)
我们设(f[i][j])表示在 (i) 到根的链上权值为$ P[w[i]+2^j]$的编号(树剖后的编号
那我们首先考虑链的情况
设终点为(E),起点为(S)
那么我们可以采用树剖函数的写法
设$x = $ 为当前深度较低的点 , (fx = top[x]),
当前需要拿(P[Ti])类宝石
我们可以枚举$id = f[x][i](i =20,19,18cdots0) $
若(fx<=id<=x)(fx,x的树剖编号)
因为树剖编号连续性 , 如果有就将x = id,重复下去即可
即可在(Theta(log^2n))解决一次询问
链的情况已经被解决了,我们考虑如何将其扩展至树上
设 (p = lca(E,S))
显然一次询问的(E -> p)端已经被我们解决了,我们需要考虑如何解决再已知(P[Ti])的情况下逆向倍增
一般地,树上倍增都是从下向上倍增,因为这根据树的性质保持了路径唯一性。
但也有特殊情况,就例如([CSP-S2019] Day2-T3),
可以沿着重儿子向下倍增,同样保证路径唯一。
对这个题来说,我们可以预处理出每一条重链上的点沿该链向下倍增且不超过这条重链的下端点的逆(f)数组,令其为(g[i][j])(就是另一种(f)数组,但是方向是沿重链向下),显然复杂度是(O(nlogn))
具体来说我们可以写一个递归函数(solve(x)),表示我们现在解决(p->E)部分中的([x,fx])这段链,
如果(x<=p<=fx)结束向下递归,特判(p)在([x,fx])之间的情况。否则递归至(solve(father[fx]))
首先在(vector[Ti])中二分查找(id),使得(fx(p)<=vector[Ti][id]<=x),然后从(id)开始$g[id][i](i =20,19,18cdots0) (,直至最后不能再倍增 这时候我们将)Ti$更新,同时结束递归传回到上一层函数。
显然一次询问复杂度已经被我们降到了(Theta(log^2n+log^2n))(显然每个询问远远达不到这个上界,再加上(O2),常数
至此树上问题已经被我们使用(O(qlog^2n))的算法解决了……
这个(DS)题康起来不是特别难,但开题的时候我首先想的是把这个问题转化为最长上升子序列问题解决,并没有认真研究((m<=300))这档部分分,因此在考试最后的30分钟里我才想起来(m<=300)提示我的是把(O(n^2))的暴力转化到和(O(m))有关的值域问题上。
而且最最最令我生气的是我考场明明想好了倍增的方法解决链上问题,然后突然一抽风把自己原本想好的给否决了Day1一下午肝完《堀与宫村》,颓废颓傻了
原本以为自己数据结构学的还算不错了,但([2021NOIO]T3)、[2021省选联考](Day2-T1)两个题还是显示了我的不足之处