闲话
这一版不老不死我已经循环播放好几天了(
真的好听!洛佬 AI 加持的声线清楚!
看到评测机天体图(?)后对比了我笔记本里的 CPU
发现我这个是 up-特高性能 的 Ryzen 5600
怪不得我电脑上跑出来的速度比各 OJ 的测速快(
《付费用户专享:i9-13900KS》
\(\prod_{i = 0}^n (p^n - p^i)\) 如何分块 CZT 做到 \(o(n)\)?
快速阶乘算法
妙妙算法。首先你需要一个任意模数多项式乘法板子,然后还需要会一点拉格朗日插值。前者是为了应对奇妙模数,后者则是可以不至于多次在点值和系数间转化。
我们需要计算的是 \(n!\pmod P\)。这个东西挺朴实的,也没啥能用的好性质,那我们直接考虑分段求解。我们假设把 \(1\sim n\) 的数分成 \(B\) 长度的段,统一计算答案。最后不满的一段可以 \(O(B)\) 计算。那统一计算就是得到这一块里的积,我们可以用一个函数描述:
\[f(B, x) = \sum_{i = 1}^B (x + i) \]假设我们能快速求得 \(f(B, 0), f(B, B), f(B, 2B), \dots, f(B, \left\lfloor\frac{n}{B}\right\rfloor B)\) 的话,我们就可以直接扫一遍这些值,在 \(O(\left\lfloor\frac{n}{B}\right\rfloor + B)\) 的复杂度内得到答案了。
其实我们可以发现 \(f(B, x)\) 是个 \(B\) 次多项式,所以我们可以简单地通过插值和多点求值在 \(O(B \log^2 B)\) 内得到答案。这个玩意最优位置不解析,所以只能硬调块长了。
有没有更快的做法?\(O(\sqrt n \log n)\) 的做法就挺好的(
我们发现,我们没必要去找到 \(f(B, x)\) 的系数,而是可以直接用点值求解点值。我们需要的是 \(d = B\) 时的 \(f(d, 0), f(d, B), f(d, 2B), \dots, f(d, dB)\),这启发我们在 \(d\) 一维上倍增。
由于最终我们只能得到 \(f(B, B^2)\),取 \(B = \left\lfloor\sqrt n\right\rfloor\)。
记数列 \(f(d, 0), f(d, B), f(d, 2B), \dots, f(d, dB)\) 为 \(\langle f(d, d) \rangle\)。
我们要想倍增 \(d\),需要的就是 \(\langle f(d, d) \rangle \to \langle f(2d, 2d) \rangle\) 和 \(\langle f(d, d) \rangle \to \langle f(d + 1, d + 1) \rangle\) 两个操作。这两个操作的复杂度不能超过 \(O(\sqrt n\log n)\),这样能得到总时间复杂度 \(O(\sqrt n\log n)\)。
分别讨论。
\(\langle f(d, d) \rangle \to \langle f(d + 1, d + 1) \rangle\)
直接扫就行了。前 \(d\) 个直接乘 \((x + d + 1)\),新的一个直接朴素计算即可。单次复杂度 \(O(d)\) 的。
\(\langle f(d, d) \rangle \to \langle f(2d, 2d) \rangle\)
我们只需要求得 \(\langle f(d, 2d)\rangle\) 和 \(f(d, d), f(d, B + d), f(d, 2B + d), \dots, f(d, 2dB + d)\),就能按位相乘得到 \(f(2d, 2d)\) 了。
不妨构造 \(h(x) = f(d, Bx)\),这样我们这两个操作就可以刻画成用 \(h\) 的一些点值得到另一些点值的操作,即给定 \(h(0), h(1), h(2),\dots, h(k)\) 且保证 \(k \ge d\),求 \(h(\Delta + 0), h(\Delta + 1), h(\Delta + 2), \dots, h(\Delta + k)\)。
然后可以转化上面的问题:
从点值序列 \(h(0), h(1), h(2), \dots, h(d)\) 得到 \(h(d + 1 + 0), h(d + 1 + 1), h(d + 1 + 2), h(d + 1 + d)\);
从 \(h(0), h(1), h(2), \dots, h(2d)\) 得到 \(h(d/B + 0), h(d/B + 1), h(d/B + 2), \dots, h(d/B + 2d)\)。
这就是 P5667 的形式。尝试应用拉格朗日插值算法,我们能写出任意点的值即
\[\begin{aligned} h(\Delta + n) &= \sum_{i = 0}^k h(i)\prod_{j \neq i} \frac{\Delta + n - j}{i - j} \\ &= \left(\prod_{j = 0}^k\Delta + n - j\right)\left( \sum_{i = 0}^k \frac{h(i)}{\Delta + n - i}\prod_{j\neq i}\frac{1}{i - j} \right) \\ &= \left(\prod_{j = 0}^k\Delta + n - j\right)\left( \sum_{i = 0}^k \frac{h(i)}{\Delta + n - i}\frac{1}{(-1)^{k - i}i!(k - i)!} \right) \end{aligned}\]我们直接把左边括号里的阶乘除过去,然后右边括号里的东西是可以卷积得到的。具体地,如果我们向右平移序列 \(n\) 位,让第 \(i + n\) 位计算 \(h(\Delta + n)\) 的值,则可以构造
\[f(i) = \frac{h(i)}{(-1)^{k - i}i! (k - i)!} \qquad g(j) = \frac{1}{\Delta - k + n} \]则有
\[h(\Delta - k + n) = \left(\prod_{j = 0}^k\Delta + n - j\right) \sum_{i + j + k} f(i) g(j) \]直接做就行了,单次复杂度 \(O(d \log d)\) 的。
因此我们就可以做到总时间复杂度 \(O(\sqrt n \log n)\) 计算原问题。
标签:dots,langle,frac,23,闲话,2d,23.1,Delta,rangle From: https://www.cnblogs.com/joke3579/p/chitchat230123.html