1. 程式人生 > >洛谷P3803 【模板】多項式乘法 [NTT]

洛谷P3803 【模板】多項式乘法 [NTT]

print ast size bits font false clu 整數 include

  題目傳送門

多項式乘法

題目描述

給定一個n次多項式F(x),和一個m次多項式G(x)。

請求出F(x)和G(x)的卷積。

輸入輸出格式

輸入格式:

第一行2個正整數n,m。

接下來一行n+1個數字,從低到高表示F(x)的系數。

接下來一行m+1個數字,從低到高表示G(x))的系數。

輸出格式:

一行n+m+1個數字,從低到高表示F(x)∗G(x)的系數。

輸入輸出樣例

輸入樣例#1:
1 2
1 2
1 2 1
輸出樣例#1:
1 4 5 2

說明

保證輸入中的系數大於等於 0 且小於等於9。

對於100%的數據: $n, m \leq {10}^6$ , 共計20個數據點,2s。

數據有一定梯度。

空間限制:256MB


  分析:

  沒錯,這是一道FFT模板,於是我們愉快地用NTT把它A了。

  平常用的較多的都是FFT,但是FFT使用的是復數,需要開double類型,常數會比較大。但有時候我們需要求的都是整形,那麽用NTT(快速數論變換)就可以把常數降低很多。具體實現理論和FFT基本無異,不過我們要把單位根換成原根,因為原根也滿足單位根的性質,最後就可得到一個結論:$w_n \equiv g^{\frac {p-1} {n}} \pmod p$。具體的理論推薦這位大佬的博客。(吐槽一句,為什麽開了O2之後不管是FFT還是NTT都反而更慢了???難道是我的代碼寫得太優秀???)

  Code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=3e6+7;
const int mod=998244353;
int n,m,lim,r[N];
int G=3,Gi=332748118;
ll a[N],b[N];
inline ll read()
{
    char ch=getchar();ll num=0;bool flag=false;
    while(ch<0||ch>9){if(ch==
-)flag=true;ch=getchar();} while(ch>=0&&ch<=9){num=num*10+ch-0;ch=getchar();} return flag?-num:num; } inline void Swap(ll &x,ll &y) { x^=y,y^=x,x^=y; } inline ll power(ll x,ll y) { ll ret=1; while(y){ if(y&1)ret=(ret*x)%mod; y>>=1;x=(x*x)%mod;} return ret; } inline void ntt(ll *A,int type) { for(int i=0;i<lim;i++) if(i<r[i])Swap(A[i],A[r[i]]); for(int mid=1;mid<lim;mid<<=1){ ll wn=power((type==1)?G:Gi,(mod-1)/(mid<<1)); for(int j=0;j<lim;j+=(mid<<1)){ ll w=1; for(int k=0;k<mid;w=(w*wn)%mod,k++){ ll x=A[j+k],y=A[mid+j+k]*w%mod; A[j+k]=(x+y)%mod; A[mid+j+k]=(x-y+mod)%mod; } } } } int main() { n=read();m=read(); for(int i=0;i<=n;i++)a[i]=(read()+mod)%mod; for(int i=0;i<=m;i++)b[i]=(read()+mod)%mod; m+=n;n=0; for(lim=1;lim<=m;lim<<=1)n++; for(int i=0;i<lim;i++) r[i]=(r[i>>1]>>1)|((i&1)<<(n-1)); ntt(a,1);ntt(b,1); for(int i=0;i<lim;i++)a[i]=(a[i]*b[i])%mod; ntt(a,-1);ll inv=power(lim,mod-2); for(int i=0;i<=m;i++) printf("%lld ",(a[i]*inv)%mod); return 0; }

洛谷P3803 【模板】多項式乘法 [NTT]