# 多项式乘法

$c_n = \sum\limits_{i=0}^{n}a_ib_{n-i}$

## 离散傅里叶变换(DFT)

$A(x)=a_0+a_1x+a_2x^2+...+a_{n-1}x^{n-1}$

$A_1(x)=a_0+a_2x+...+a_{n-2}x^{\frac{n}{2}-1}$

$A_2(x)=a_1+a_3x+...+a_{n-1}x^{\frac{n}{2}-1}$

$k<\dfrac{n}{2}$，将任意$\omega_n^k$代入$A(x)$可得$$A(\omega_nk)=A_1(\omega_{\frac{n}{2}}{k})+\omega_nkA_2(\omega_{\frac{n}{2}}k)$$

## 离散傅里叶逆变换(IDFT)

$B(x)=y_0+y_1x+...+y_{n-1}x^{n-1}$

\begin{aligned} z_k &= \sum_{i=0}^{n-1}y_i(\omega_n^{-k})^i\\ &= \sum_{i=0}^{n-1}(\sum_{j=0}^{n-1}a_j*\omega_n^{ij})(\omega_n^{-k})^i\\ &= \sum_{i=0}^{n-1}\sum_{j=0}^{n-1}a_j*(\omega_n^{j-k})^i\\ &= \sum_{i=0}^{n-1}a_j(\sum_{j=0}^{n-1}(\omega_n^{j-k})^i)\\ \end{aligned}

$\sum_{j=0}^{n-1}(\omega_n^{j-k})^i=\dfrac{(\omega_n^{j-k})^n-1}{\omega_n^{j-k}-1}$

$j \neq k$时，$\sum_{j=0}^{n-1}(\omega_n^{j-k})^i=0$

$j = k$时，$\sum_{j=0}^{n-1}(\omega_n^{j-k})^i=n$

$z_k = \sum\limits_{i=0}^{n-1}a_j(\sum\limits_{j=0}^{n-1}(\omega_n^{j-k})^i) = na_k$

$a_k=\dfrac{z_k}{n}$

# NTT

$FFT$需要用复数运算，存在精度问题。$NTT$（快速数论变换）是模意义下的$FFT$，可以避免精度问题，还快了不少。而其实现原理就是用模数的原根来代替单位根。

/*DennyQi 2019*/
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
const int N = 4000010;
const int P = 998244353;
const int INF = 0x3f3f3f3f;
inline int Max(const int& a, const int& b){ return a>b?a:b; }
inline int Min(const int& a, const int& b){ return a<b?a:b; }
inline int mul(const int& a, const int& b){ return 1ll*a*b%P; }
inline int add(const int& a, const int& b){ return (a+b>=P)?a+b-P:a+b; }
inline int sub(const int& a, const int& b){ return (a-b<0)?a-b+P:a-b; }
int x = 0, w = 0; char c = getchar();
while(c^'-' && (c<'0' || c>'9')) c = getchar();
if(c=='-') w = 1, c = getchar();
while(c>='0' && c<='9') x = x*10+c-'0', c = getchar(); return w?-x:x;
}
int n,m,lim=1,len,invn,a[N],b[N],r[N];
inline int qpow(int x, int y){
int res = 1;
while(y){
if(y & 1) res = mul(res,x);
y >>= 1, x = mul(x,x);
}
return res;
}
inline void NTT(int* a, int Tp){
for(int i = 1; i < lim; ++i) if(i < r[i]) swap(a[i],a[r[i]]);
for(int i = 1; i < lim; i <<= 1){
int w0 = Tp ? qpow(qpow(3,(P-1)/i/2),P-2) : qpow(3,(P-1)/i/2);
for(int j = 0; j < lim; j += (i<<1)){
int w = 1;
for(int k = j; k < j+i; ++k){
const int tmp = mul(w,a[k+i]);
a[k+i] = sub(a[k],tmp);
w = mul(w,w0);
}
}
}
}
int main(){
for(int i = 0; i <= n; ++i) a[i] = read();
for(int i = 0; i <= m; ++i) b[i] = read();
len = n+m+1;
while(lim <= len) lim <<= 1;
for(int i = 1; i < lim; ++i) r[i] = (r[i>>1]>>1)|((i&1)?(lim>>1):0);
NTT(a,0);
NTT(b,0);
for(int i = 0; i < lim; ++i) a[i] = mul(a[i],b[i]);
NTT(a,1);
invn = qpow(lim,P-2);
for(int i = 0; i < len; ++i) a[i] = mul(a[i],invn);
for(int i = 0; i < len; ++i) printf("%d ",a[i]);
printf("\n");
return 0;
}

posted @ 2019-07-11 14:23  DennyQi  阅读(519)  评论(0编辑  收藏  举报