半在线卷积
Nine_Suns
·
·
个人记录
在搞多项式题时,不免化出一些不易操作的式子,如 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;
}
```