UOJ33 【UR #2】树上GCD
有一棵 \(n\) 个结点的有根树 \(T\)。结点编号为 \(1 \dots n\),其中根结点为 \(1\)。
树上每条边的长度为 \(1\)。我们用 \(d(x,y)\) 表示结点 \(x, y\) 在树上的距离,\(\text{LCA}(x,y)\) 表示 \(x, y\) 的最近公共祖先(即树中最深的既是 \(v\) 的祖先也是 \(u\) 的祖先的结点)。
对于两个结点 \(u,v\) \((u\neq v)\),令 \(a=\text{LCA}(u,v)\),定义 \(f(u,v)=\gcd (d(u,a),d(v,a))\)。其中 \(\gcd(x,y)\) 表示 \(x,y\) 的最大公约数,特别地, \(\gcd(0,x)=\gcd(x,0)=x\) \((x\neq 0)\)。
对于所有 \(i \in \{1,2,\cdots,n-1 \}\),求出有多少对 \((u,v)\) \((u < v)\),满足 \(f(u,v) = i\)。
\(n \leq 200000\)。
动态规划
根号分治
启发式合并
首先,\(\gcd\) 可以直接转化为有多少对数的 \(\gcd\) 是 \(x\) 的倍数,最后只要容斥一下 / 类似狄利克雷前缀和的方式 处理一下即可。
- 对于 \(x \le \sqrt{n}\),我们可以枚举 \(x\),然后暴力遍历所有点,通过将其贡献转移到其 \(x\) 级祖先上,我们可以统计出 \(f(u)\) 表示距离 \(u\) 距离为 \(x\) 的倍数的点的个数,然后就可以计算贡献了。为了方便统计答案,我们可以利用一个辅助数组防止一对属于同一个子树的点被统计。(暂缓上传贡献,遍历到每个点的时候,将辅助数组贡献和其父亲贡献合并),这部分复杂度为 \(\mathcal O(n\sqrt{n})\)。
- 对于 \(x > \sqrt{n}\),我们可以直接记 \(f(x, i)\) 表示距离 \(x\) 为 \(i\) 的点的个数,然后启发式合并,至于贡献可以在合并的时候暴力枚举倍数计算,因为只有 \(x\) 和父亲的 \(siz\) 都 \(> \sqrt{n}\) 的时候才会发生,于是最多发生 \(\sqrt{n}\) 次,这部分复杂度为 \(\mathcal O(n\sqrt{n})\)。
注意如果两个点成祖先关系,那么比较麻烦,可以暂不统计,最后再计算。
代码。
本博客作者:Werner_Yin(https://www.cnblogs.com/werner-yin/) ,转载时请注明出处,谢谢支持!