洛谷P4245 【模板】MTT

洛谷 P4245 【模板】MTT

题目背景

模板题,无背景

题目描述

给定 $2$ 个多项式 $F(x), G(x)$ ,请求出 $F(x) * G(x)$ 。

系数对 $p$ 取模,且不保证 $p$ 可以分解成$ p = a \cdot 2^k + 1$ 之形式。

输入输出格式

输入格式:

输入共 $3$ 行。
第一行 $3$个整数$ n, m, p$ ,分别表示 $F(x), G(x)$ 的次数以及模数 $p$ 。
第二行为 $n+1$ 个整数, 第 $i$ 个整数 $a_i$ 表示 $F(x)$ 的 $i-1$ 次项的系数。
第三行为 $m+1$个整数, 第 $i$ 个整数 $b_i$ 表示 $G(x)$ 的 $i-1$ 次项的系数。

输出格式:

输出$ n+m+1$ 个整数, 第 $i$ 个整数 $c_i$ 表示$ F(x) * G(x)$ 的 $i-1$次项的系数。

说明

$1 \leq n \leq 10^5, 0 \leq a_i, b_i \leq 10^9, 2 \leq p \leq 10^9 + 9$


拆系数FFT裸题。第一次写拆系数FFT,主要是想记录一下基本知识点。

首先,遇到这种模数不是 $998244353$ 这种好的$a \cdot 2^k+1$形式的质数时显然不能上NTT。为什么不上FFT呢?因为结果很可能非常大,以double和long double 直接存储精度爆炸。因此,拆系数FFT就是通过把原来的结果拆成精度在可接受范围内的几个数,再通过运算得到答案

具体操作过程如下:

设$F(x)=\sum_{i=0}^{n-1}a_ix^i$,$G(x)=\sum_{i=0}^{n-1}b_ix^i$,考虑求$F(x)*G(x)$各项系数模$p$的值。

首先将$a_i$和$b_i$对$p$取模作为新的$a_i$,$b_i$。

设$M=\lfloor \sqrt p \rfloor$,现在把$a_i$和$b_i$都写成$cM+d$的形式,即对$M$做了一次带余除法。

下面设$a_i=c_iM+d_i$,$b_i=e_iM+f_i$。代入原来的答案式得到第$k$项系数的表达式:

这样处理之后,$c_ie_{k-1},c_if_{k-i},d_ie_{i-k},d_if_{k-i}$在大多数时候都在long long的范围内,而$M$和$M^2$都不会超过$p$,所以如果我们分别对$c_i$和$e_i$、$c_i$和$f_i$、$d_i$和$e_i$、$d_i$和$f_i$做FFT就可以得到答案。

然而这样做的话直接搞需要4次DFT和3次IDFT(其中$c_if_{k-i}$和$d_ie_{i-k}$可以分别做1次DFT,点值相加后再做1次IDFT)。

这里有一种优化常数的方法,来自毛啸的集训队论文:

我们对$a’_i=c_i i +d_i$和$b’_i=e_i i +f_i$(这里$c_i$和$e_i$之后的$i$是虚数单位)做一次卷积,答案的第$k$项就是:

也就是说,第$k$项的虚部就储存了$\sum_{i=0}^k(c_if_{k-i}+d_ie_{i-k})$,实部储存了$\sum_{i=0}^k(d_if_{k-i}-c_ie_{k-i})$,只需要再求出$d_i$和$f_i$的卷积或者$c_i$和$e_i$的卷积就做完了。这样做就需要4次DFT和2次IDFT。


不优秀且丑的代码,似乎不用long double 的话精度过不去。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include<stdio.h>
#include<algorithm>
#include<cstring>
#include<complex>
#include<cmath>
#define MAXN 800005
#define db long double
#define ll long long
using namespace std;
const db Pi=atan(1.0)*4.0;

int n=1,N,M,P,SQ,A[MAXN],B[MAXN];
ll Ans[MAXN];
complex<db>AA[MAXN],BB[MAXN],CC[MAXN],DD[MAXN];

complex<db>w[MAXN];

void FFT(complex<db>A[],int n,int ty){
int i,j,k,m;
complex<db>t0,t1;
for(i=0;i<n;i++){
for(j=0,m=1,k=i;m<n;m<<=1,j=(j<<1)|(k&1),k>>=1);
if(i<j)swap(A[i],A[j]);
}
w[0]=1;
for(m=1;m<n;m<<=1){
t0=exp(complex<db>(0,ty*Pi/m));
for(i=1;i<m;i++)w[i]=w[i-1]*t0;
for(k=0;k<n;k+=m<<1)
for(i=k;i<k+m;i++){
t0=A[i];t1=A[i+m]*w[i-k];
A[i]=t0+t1;A[i+m]=t0-t1;
}
}
if(ty==1)return;
t0=1.0/n;
for(i=0;i<n;i++)A[i]=A[i]*t0;
}

int main(){
int i;
scanf("%d%d%d",&N,&M,&P);
for(i=0;i<=N;i++)scanf("%d",&A[i]),A[i]%=P;
for(i=0;i<=M;i++)scanf("%d",&B[i]),B[i]%=P;
SQ=floor(sqrt(P));
while(n<N+M+4)n<<=1;
for(i=0;i<=N;i++)AA[i]=complex<db>(A[i]%SQ,A[i]/SQ);
for(i=0;i<=M;i++)BB[i]=complex<db>(B[i]%SQ,B[i]/SQ);

FFT(AA,n,1);FFT(BB,n,1);
for(i=0;i<n;i++)AA[i]*=BB[i];
FFT(AA,n,-1);
for(i=0;i<=N;i++)CC[i]=complex<db>(A[i]/SQ,0);
for(i=0;i<=M;i++)DD[i]=complex<db>(B[i]/SQ,0);
FFT(CC,n,1);FFT(DD,n,1);
for(i=0;i<n;i++)CC[i]*=DD[i];
FFT(CC,n,-1);

for(i=0;i<n;i++){
ll tmp=(ll)floor(CC[i].real()+0.5)%P*SQ%P*SQ%P;
tmp=(tmp+(ll)floor(AA[i].imag()+0.5)%P*SQ%P)%P;
tmp=(tmp+(ll)((ll)floor(AA[i].real()+0.5)%P+(ll)floor(CC[i].real()+0.5)%P)%P)%P;
Ans[i]=(tmp%P+P)%P;
}
for(i=0;i<=N+M;i++)printf("%lld ",Ans[i]);
}