考虑 dp,设 fu 为喂饱 u 子树所需的最小次数。对于叶子,令 fu=au;对于有两个儿子 v0,v1 的非叶子,fu=2max(fv0,fv1)−[fv0=fv1],因为两边次数不相等时,我们可以先操作需要次数更多的。
假设 gu=fu−1,则转移变为 gu=2max(gv0,gv1)+[gv0=gv1]。令 depu 表示 u 的深度(dep1=0),Fu=gu×2depu,那么 f1=F1+1,Fu=max(Fv0,Fv1)+2depu[Fv0=Fv1]。
可以发现一个性质:Fu 的二进制表示最多包含 log2nA 个 1(A=maxai)。通过归纳法证明:假设可以导致 Fu 有 k 个 1 的树的最小大小为 d(k) . 那么,当 k≤log2A 时,d(k)=1;否则,d(k)≥2d(k−1),因为 Fv0=Fv1 时才会出现新的 1。
维护一个由二元组 (u,x) 组成的集合 S,其中 x 是 Fu 的一个下界。最初,将所有满足 au>0 的叶子 u 的二元组 (v,2depu(au−1)) 放入 S,如果 S 中同时存在 (p,x) 和 (q,x)(p=q),则将 (r,x+2depr) 放入 S,其中 r=LCA(p,q),且 2depr 一定是一个新位(即加法一定不会发生进位)。
这样做为什么是正确的?考虑证明一个点的实际值一定不小于它的任何一个下界。对于任意一个 p 的祖先 o(对于 q 同理),显然有 Fo≥Fp,则 x≤Fp,Fq≤Fr;若存在一个点 z 在 p→r 转移的路径上,满足 Fz>Fp,那么 Fz 的一个下界是 Fp+2depz≥Fp+2depr,因为 depz≥depr。
那么答案是 S 中所有数对中最大的 x+1(如果 S 是空的,答案显然是 0)。
具体维护的时候,每当加入一个二元组 (u,x) 时,我们只需要考虑 x 相同,且 u 与其 dfs 序相邻的两个 u,这样才能使其深度最大。设它们分别为 p,q,则我们要考虑 y=LCA(u,p),z=LCA(u,q) 两个点,在这两者中取深度更大者,因为深度小的一定是深度大的的祖先,不需要考虑。设 depy≥depz,则我们加入 (y,x+2depy),并重复上一流程。若找不到任意一个 y 就跳出。
因为位数不超过 log2nA,所以一次加入操作最多会插入 log2nA 个二元组,如果我们使用 std::vector<int>
维护有 1 的所有位置,并用 std::set
维护二元组,则 std::set
的时间复杂度是 O(lognlog2nA)。共操作 O(log2nA) 次,则单次加入或删除的时间复杂度为 O(lognlog22nA)。若要更改 au,只需从 S 中删除旧的 au 所更新到的顶点对(类似于添加),然后添加新的 au 所更新到的顶点对。总时间复杂度 O((n+q)lognlog22nA),不知道为什么可以通过。
code