快速数论变换NTT学习笔记
前言
在这篇文章中我介绍了什么是
只要毒瘤出题人原因,那就可以
Tips:
根据《NOI大纲》的内容,卡精度和卡常数通常是不允许的。
所以
通常不会寄,不必过度焦虑。 但是
本身并不难。
这时候我们就需要引入快速数论变换
首先我们要明确一个方向,就是
- 消去原理:
- 对称原理:
也就是说,只要满足以上条件,也就可以用类似的方法实现。
我们发现,数论里的原根,和这个很像啊!所以直接使用即可。
代码实现
经典的模数
直接替换
#include<bits/stdc++.h> #define LL long long using namespace std; const LL N=1e6; const LL mod=998244353; const LL G=3; LL ksm(LL x,LL y) { LL ans=1; while(y) { if(y&1)ans=ans*x%mod; x=x*x%mod; y>>=1; } return ans; } LL n,m,Gi,lim,len=1,r[N],x,ans[N]; LL a[N],b[N]; void FFT(LL *a,LL inv) { for(int i=0;i<=len-1;i++) { if(i<r[i])swap(a[i],a[r[i]]); } for(int l=2;l<=len;l*=2) { LL omg=ksm(G,(mod-1)/l); if(inv)omg=ksm(Gi,(mod-1)/l); LL m=l/2; for(int j=0;j<=len-1;j+=l) { LL x=1; for(int i=0;i<=m-1;i++) { LL t=x; t=a[i+j+m]*t%mod; a[i+j+m]=(a[i+j]-t+mod)%mod,a[i+j]=(a[i+j]+t)%mod; x=x*omg%mod; } } } } int main() { scanf("%lld%lld",&n,&m); Gi=ksm(G,mod-2); while(len<=n+m)len*=2,lim++; for(int i=0;i<=len-1;i++) { LL sum=0; for(int j=0;j<=lim-1;j++)sum|=((i>>j)&1)<<(lim-j-1); r[i]=sum; } for(int i=0;i<=n;i++) { scanf("%lld",&a[i]); } for(int i=0;i<=m;i++) { scanf("%lld",&b[i]); } FFT(a,0),FFT(b,0); for(int i=0;i<=len-1;i++) { a[i]=a[i]*b[i]%mod; } FFT(a,1); LL inv=ksm(len,mod-2); for(int i=0;i<=n+m;i++) { ans[i]=a[i]*inv%mod; printf("%lld ",ans[i]); } return 0; }
扩展
通常情况下在模意义都会使用原根来替换单位根,比如单位根反演就可以使用原根。
这里简单展示一下单位根反演的结论:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步