多项式的一些运用 (1)
写一些我在多项式方面的一些发现。
树上路径长度统计
题意:给定 \(n\) 个点的边权为 \(1\) 的无根树,对每个 \(k\) 求树上有多少条长度为 \(k\) 的路径。
经典的点分治算法是 \(\mathcal O(n\log^2 n)\) 的;但我发现可以 \(\mathcal O(n\log n)\)!
做法
考虑长链剖分:对于一条路径,在其 LCA 所在的重链上统计贡献。
此时考虑一条长链的贡献。对于该链上的第 \(i\) 个点 \(v_i\),统计出 \(v_i\) 轻子树及它自己中,距离它为每个值的点的个数,并用多项式 \(P_i(x)\) 来表示它。
此时我们可以将问题转化为 \(\sum_{i < j} P_i(x)P_j(x)x^{j-i}\)。
考虑先去掉 \(i < j\) 的限制,去掉后就是求 \(\sum_{i,j}P_i(x)P_j(x)x^{j-i} = (\sum_i P_i(x)x^{-i})(\sum_{j}P_j(x)x^{j})\),可以一遍卷积解决;
现在考虑减去 \(i\ge j\) 的贡献。此时要求 \(\sum_{i < j} P_i(x)P_j(x)x^{-(i-j)}\)。此时我们只需要考虑非负次数的部分。
考虑在 \(deg(P_i(x))\) 和 \(deg(P_j(x))\) 中度数较大的一边计算贡献,不妨计算 \(P_i(x)\) 的贡献。
注意到此时我们对答案的贡献只会在 \([1,2deg P_i(x)]\) 中,所以我们实际上只需要提取 \(\sum_{j > i, deg P_j(x) <= deg P_i(x)} P_j(x)x^j\) 中的一个长度为 \(\mathcal O(deg P_i(x))\) 区间做卷积。获取我们想要做卷积的数组是一个二维数点问题。
单次复杂度 \(\mathcal O((\sum P_i(x)) \log n)\),总复杂度 \(\mathcal O(n \log n)\)。
CCPC Final 2023 B
题意:给定 \(n\),求
这题原标算做法是 \(\mathcal O(n \sqrt n)\) 的;我给出了 \(\mathcal O(n\log^2 n)\) 的算法。
做法
取 \(B = \sqrt n\)。
对于 \(k< B\),可以分治 FFT 维护分式,最后一遍求逆算出答案。
对于 \(k\ge B\):
这也是可以分治 FFT 的形式,可以分治维护分式。
UOJ696
题意:给定 \(n,r\),求 \((\prod_{i < r} \frac{1}{1-x^{i}}) \sum_{i} \frac{x^{ir}}{\prod_{j \le i} (1-x^{j})^2} \bmod x^{n+1}\)。
题解问大伙有没有 \(\tilde O(n)\) 的做法。答案是有,而且做法挺直接。
做法
首先,这题有 \(\mathcal O(((\frac{n}{r})^2+n)\log^2 n)\) 的做法:\(\sum\) 左边的部分是容易算的;对于右边的部分,我们只需枚举 \(i\le \frac{n}{r}\)。此时我们可以对 \(i\) 分治维护分式。
-
官方题解的做法7给出了一个 \(\tilde O(r n^{1/2})\) 的做法,这个做法拼接上面做法可以直接得到 \(\tilde O(n)\) 的做法;
-
官方题解的做法6给出了瓶颈在于递推 \(F_r(x)=2F_{r-1}(x)+(x^{r-2}-1)F_{r-2}(x)\) 的做法,这个也可以通过分治 FFT 维护矩阵做到 \(\mathcal O(r^2\log^2 n)\),总复杂度 \(\mathcal O(n\log^2 n)\)。
(UPD ON 4/15: 再更一个题)
XMAS24C
题意:给定度数为 \(m\) 的多项式 \(f\) 和大小为 \((n_1,n_2,...,n_k)\) 的多元多项式 \(g\)。求 \(f(g)\) 在模 \((x^{n_1},x^{n_2},...,x^{n_k})\) 下的值。
设 \(N = \prod n_i\),我们不妨假定 \(N,M\) 同阶(我们可以通过平移让 \(g(0)=0\),此时 \(f\) 只用保留前 \(N\) 项)。原标算是 \(\mathcal O(\frac{N\log^3 N}{\log\log N})\) 的;我给出了 \(\mathcal O(N\log^2 N)\) 的做法。
做法
不妨 \(n_1\le n_2\le ... \le n_k\)。
类似二元的做法,考虑一个一个去除每个变元的影响。
考虑在 \(f(0,...)\) 处泰勒展开,得到:
首先,我们对于每个 \(i\),递归地计算每个 \(f^{(i)}(g(0,...))\)。
接下来,我们可以用多项式复合来处理这个问题。
我们把 \(x_1\) 当作变元。对于后面的 \(x_2,x_3,...\),我们将其看成是一个环。这里环上的加法就是直接加和,乘法就是在 \(\bmod (x_2,x_3,...,x_k)\) 意义下做多元多项式乘法。我们做的实际上就是这个环上的多项式复合问题。
在做多项式复合的时候,我们需要 \(x_1 \log x_1\) 次环上乘法操作和 \(x_1 \log^2 x_1\) 次环上加法操作。
所以我们总共花费了 \(N k \log x_1 \log N + N \log^2 x_1\) 的代价递归到了子问题。这里 \(k\log x_1=\log x_1^k\le \log N\),这一部分递归的代价至少还在 \(N\log^2 N\) 之内。
看起来前面那一项会很大;实际上由于我们每一层要解决的问题总规模在变小,复杂度实际上是对的。
对于第 \(i\) 层子问题,我们始终只需要 \(1+\sum_{j<i}x_j\) 次计算(即对每个 \(0\le p\le \sum_{j<i}x_j\) 计算 \(f^{(p)}(g(0,0,...,x_{i},x_{i+1},...,x_k))\))。
对于 \(i>1\),我们考虑前面那部分复杂度:
\(\sum \frac{i}{2^i}\) 是常数,所以总和是 \(\mathcal O(N\log^2 N)\)。