半在线卷积

· · 个人记录

在搞多项式题时,不免化出一些不易操作的式子,如 F'=F\cdot G+1 之类的,没办法套模板解决,半在线卷积可以在 O(n\log^2n) 内很好的解决它们。

半在线卷积实质上是强制中序遍历\rm cdq 分治。它的步骤大约是这样:

为了方便处理,首先将 n 补足至 2 的次幂。

设正在处理的区间是 [l,r),mid=\frac {l+r}2,那么应先递归处理 [l,mid),再处理 [l,mid)[mid,r) 的贡献,最后处理 [mid,r)

接下来通过几个问题知道半在线卷积的具体方法。

对于 F_n=\sum\limits_{i=0}^{n}F_iG_{n-i},其中 F_0=1,G_0=0

设正在处理的区间是 [l,r),mid=\frac {l+r}2。先递归处理 [l,mid),此时 [l,mid)F 的值均已算出,我们用 F_{[l,mid)} 卷上 G_{[0,r-l)},并将得到的多项式 [mid,r) 项的值累加到 F 中。这就处理了 [l,mid)[mid,r) 的贡献。然后递归处理 [mid,r) 即可。

复杂度 O(n\log^2n)

一个值得注意的地方是,在用 F_{[l,mid)}G_{[0,r-l)} 时,是一个 \frac {r-l}2 次多项式卷 r-l 次多项式的操作,由于 \rm NTT 是循环卷积,若在 r-l 的范围内直接卷,最后的 \frac {r-l}2 次会破坏掉前面的 \frac {r-l}2 次,而我们只需 [mid,r) 项的值,正好是没有被破坏的那部分,于是可以用 r-l 为范围卷,节省一部分常数。

我们尝试原来的方法,发现关键在于 $[l,mid)$ 对 $[mid,r)$ 的贡献部分。 如果我们用 $F_{[l,mid)}$ 卷 $G_{[0,r-l)}$ 的话,会发现,在 $l=0$ 时 $r-l>mid$,需要用的 $G$ 还没有算出! 我们分类讨论一下: 在 $l=0$ 时,直接用 $F_{[l,mid)}\cdot G_{[0,mid)}$ 贡献,此时漏掉了一部分贡献。 在 $l>0$ 时,用 $F_{[l,mid)}\cdot G_{[0,r-l)}+F_{[0,r-l)}\cdot G_{[l,mid)}$ 贡献。前一部分的贡献是常规的。后一部分是在算出 $G_{[l,mid)}$ 后对 $l=0$ 时贡献的补充。 复杂度仍然是 $O(n\log^2n)$。 ___ 用半在线卷积做多项式 $\rm exp$ 是一件很棒的事情。 先进行简单的推式子,$G(x)=e^{F(x)},\ln G(x)=F(x),G'(x)=F'(x)G(x)$。 也就是 $(n+1)G_{n+1}=\sum\limits_{i=0}^n (i+1)F_{i+1}G_{n-i}$,变一下改成 $nG_n=\sum\limits_{i=1}^{n}iF_iG_{n-i}$。常数可以在分治边界乘上。 直接 $O(n\log^2n)$ 带走。~~甚至跑得过牛迭的一只 log~~ ```cpp int n, a[N], b[N], g[N]; void solve (int *f, int l, int r) { if (l+1 == r) { if (l == 0) f[l] = 1; else f[l] = 1ll*qpow(l)*f[l]%mod; return; } int mid = l+r>>1; solve(f, l, mid); for (int i = l;i < mid;i++) a[i-l] = f[i]; for (int i = mid;i < r;i++) a[i-l] = 0; for (int i = 0;i < r-l;i++) b[i] = g[i]; poly::init(r-l); poly::NTT(a, r-l, 1); poly::NTT(b, r-l, 1); for (int i = 0;i < r-l;i++) a[i] = 1ll*a[i]*b[i]%mod; poly::NTT(a, r-l, -1); for (int i = mid;i < r;i++) f[i] = (f[i]+a[i-l])%mod; solve(f, mid, r); } int f[N]; int main () { scanf("%d", &n); int m = 1; while (m < n) m <<= 1; for (int i = 0;i < n;i++) scanf("%d", &g[i]), g[i] = 1ll*g[i]*i%mod; solve(f, 0, m); for (int i = 0;i < n;i++) printf("%d ", f[i]); return 0; } ```