关系变成没关系,问题是没问题。
——草东没有派
|

DengDuck

园龄:3年7个月粉丝:37关注:22

快速数论变换NTT学习笔记

前言

这篇文章中我介绍了什么是 FFT ,但是在文中我们也说了一嘴这玩意是有精度误差的,三角函数和复数导致我们不得不进行取整操作。

只要毒瘤出题人原因,那就可以 Hack FFT

Tips:

根据《NOI大纲》的内容,卡精度和卡常数通常是不允许的。

所以 FFT 通常不会寄,不必过度焦虑。

但是 NTT 本身并不难。

这时候我们就需要引入快速数论变换 NTT(Fast Number Theory Transform,不过我们通常省略那个 F)。

首先我们要明确一个方向,就是 FFT 的原理是单位根的几个性质:

  • 消去原理: ωtntk=ωnk
  • 对称原理:ωnk=ωnk+n2
  • ωnk=(ωn1)k
  • ωniωnj(ij)

也就是说,只要满足以上条件,也就可以用类似的方法实现。

我们发现,数论里的原根,和这个很像啊!所以直接使用即可。

代码实现

经典的模数 998244353 的原根为 3,这是常用的组合。

直接替换 ω 即可。

#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;
}

扩展

通常情况下在模意义都会使用原根来替换单位根,比如单位根反演就可以使用原根。

这里简单展示一下单位根反演的结论:

[n|k]=1ni=0n1ωnki

posted @   DengDuck  阅读(55)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起