数论变换NTT

前置知识

  1. FFT:NTT的思想和FFT一样([FFT介绍](%3Ca href="https://www.cnblogs.com/guoshaoyang/p/10957854.html"%3Ehttps://www.cnblogs.com/guoshaoyang/p/10957854.html%3C/a%3E))

概述

数论变换,即NTT(Number Theory Transformation?),是基于数论域的FFT,一般我们默认FFT为负数域上的快速傅里叶变换,和NTT区分。

我们知道,FFT是利用单位复根的周期性,以\(\Theta(N \log N)\)的复杂度计算\(N\)组多项式的值。NTT其实就是在数论域上的FFT,它利用素数取模的周期性,达到了和FFT一样的效果。

原根

若\(a\)模\(P\)的阶等于\(φ(P)\),则称\(a\)为模\(P\)的一个原根(这个定义不重要)。

对于质数\(P\),若\(g\)是\(P\)的原根,那么\(g^i \mod P\)的结果两两不同;或者说\(g^t = 1 (\mod P)\)当且仅当指数为\(t=P-1\)的时候成立。

我们发现,原根的性质和单位负数根的性质一一对应,都满足周期性和周期内互异性,故可以用来代入和插值。

NTT

我们有离散傅里叶变换公式

\[X_k=\sum^{N-1}_{n=0}{x_ne^{-\frac{2\pi i}{N}nk}} \ \ \ \ \ \ \ \ \ \ \ \ (k=0,1,...,N-1)
\]

和逆变换公式

\[X_k=\frac{1}{N}\sum^{N-1}_{n=0}{x_ne^{\frac{2\pi i}{N}nk}} \ \ \ \ \ \ \ \ \ \ \ \ (k=0,1,...,N-1)
\]

我们只需要用原根\(g^{\frac{P-1}{N}} (\mod P)\)替代单位复根\(e^{-\frac{-2\pi i}{N}}\),就可以得到

\[X_k=\sum^{N-1}_{n=0}{x_ng^{\frac{P-1}{N}nk}}(\mod P) \ \ \ \ \ \ \ \ \ \ \ \ (k=0,1,...,N-1)
\]

\[X_k=\frac{1}{N}\sum^{N-1}_{n=0}{x_ng^{-\frac{P-1}{N}nk}}(\mod P) \ \ \ \ \ \ \ \ \ \ \ \ (k=0,1,...,N-1)
\]

实现

和FFT的模板几乎一样,用原根\(g^{\frac{P-1}{N}} (\mod P)\)替代单位复根\(e^{-\frac{-2\pi i}{N}}\)即可

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int INF=1e9+7,MAXN=3e6+10/*Min:2^20+10*/;
const LL BASE=3,MOD=998244353,INV=332748118;
inline LL fpm(LL base,LL p){
LL ret=1;
while(p){
if(p&1)
ret=ret*base%MOD;
base=base*base%MOD;
p>>=1;
}
return ret%MOD;
}
int N,M,limit=1,lg,rev[MAXN];
inline void NTT(LL *a,int type){
for(int i=0;i<limit;i++)
if(i<rev[i])
swap(a[i],a[rev[i]]);
for(int mid=1;mid<limit;mid<<=1){
int len=mid<<1/*n*/;
LL Wn=fpm(type==1?BASE:INV/*BASE^type*/,(MOD-1)/len);
for(int j=0;j<limit;j+=len){
LL w=1;
for(int k=0;k<mid;k++){
int x=a[j+k],y=w*a[j+k+mid]%MOD;
a[j+k]=(x+y)%MOD;
a[j+k+mid]=(x-y+MOD)%MOD;
w=w*Wn%MOD;
}
}
}
}
LL a[MAXN],b[MAXN];
int main(){
scanf("%d%d",&N,&M);
for(int i=0;i<=N;i++){
scanf("%lld",a+i);
a[i]=(a[i]+MOD)%MOD;
}
for(int i=0;i<=M;i++){
scanf("%lld",b+i);
b[i]=(b[i]+MOD)%MOD;
}
while(limit<=N+M){
limit<<=1;
lg++;
}
for(int i=0;i<limit;i++)
rev[i]=(rev[i>>1]>>1)|((i&1)<<(lg-1));
NTT(a,1);
NTT(b,1);
for(int i=0;i<limit;i++)
a[i]=a[i]*b[i]%MOD;
NTT(a,-1);
LL limit_inv=fpm(limit,MOD-2);
for(int i=0;i<=N+M;i++)
printf("%lld ",a[i]*limit_inv%MOD);
return 0;
}

复杂度

时间

和FFT一样,是\(\Theta(N \log N)\),在数据较小的情况下比FFT快,但因为其中有快速幂运算,如果数字太大则会导致TLE

空间

和FFT一样,是\(\Theta(N)\),但注意数组大小不是\(N+M\),而是不小于\(N+M\)的最小的\(2\)的幂

范围(素数选取)

我们需要一个大素数来取模,这个素数要大于最终输出的答案(但如果太大,就需要使用快速乘)

下面给出常用的几个素数,原根及原根的逆元

素数 原根 逆元
65537 3 21846
786433 10 235930
5767169 3 1922390
7340033 3 2446678
23068673 3 7689558
104857601 3 34952534
167772161 3 55924054
469762049 3 156587350
998244353 3 332748118
1004535809 3 334845270
2013265921 31 64944062
2281701377 3 760567126
3221225473 5 1932735284
75161927681 3 25053975894
77309411329 7 22088403237
206158430209 22 84337539631
2061584302081 7 589024086309
2748779069441 3 916259689814
6597069766657 5 2638827906663
39582418599937 5 15832967439975
79164837199873 5 47498902319924
263882790666241 7 150790166094995
1231453023109120 3 410484341036374
1337006139375610 3 445668713125206
3799912185593850 5 1519964874237540
4222124650659840 19 888868347507335
7881299347898360 6 1313549891316390
31525197391593400 3 10508399130531100
180143985094819000 6 30023997515803300
1945555039024050000 5 1167333023414430000
4179340454199820000 3 1393113484733270000

NTT数论变换的更多相关文章

  1. 快速傅里叶变换FFT& 数论变换NTT

    相关知识 时间域上的函数f(t)经过傅里叶变换(Fourier Transform)变成频率域上的F(w),也就是用一些不同频率正弦曲线的加 权叠加得到时间域上的信号. \[ F(\omega)=\m ...

  2. 多项式 之 快速傅里叶变换(FFT)/数论变换(NTT)/常用套路【入门】

    原文链接https://www.cnblogs.com/zhouzhendong/p/Fast-Fourier-Transform.html 多项式 之 快速傅里叶变换(FFT)/数论变换(NTT)/ ...

  3. 高速数论变换(NTT)

    今天的A题.裸的ntt,但我不会,于是白送了50分. 于是跑来学一下ntt. 题面非常easy.就懒得贴了,那不是我要说的重点. 重点是NTT,也称高速数论变换. 在非常多问题中,我们可能会遇到在模意 ...

  4. 从傅里叶变换(FFT)到数论变换(NTT)

    FFT可以用来计算多项式乘法,但是复数的运算中含有大量的浮点数,精度较低.对于只有整数参与运算的多项式,有时,\(\text{NTT(Number-Theoretic Transform)}\)会是更 ...

  5. 【算法】快速数论变换(NTT)初探

    [简介] 快速傅里叶变换(FFT)运用了单位复根的性质减少了运算,但是每个复数系数的实部和虚部是一个余弦和正弦函数,因此系数都是浮点数,而浮点数的运算速度较慢且可能产生误差等精度问题,因此提出了以数论 ...

  6. Algorithm: 多项式乘法 Polynomial Multiplication: 快速傅里叶变换 FFT / 快速数论变换 NTT

    Intro: 本篇博客将会从朴素乘法讲起,经过分治乘法,到达FFT和NTT 旨在能够让读者(也让自己)充分理解其思想 模板题入口:洛谷 P3803 [模板]多项式乘法(FFT) 朴素乘法 约定:两个多 ...

  7. 「算法笔记」快速数论变换(NTT)

    一.简介 前置知识:多项式乘法与 FFT. FFT 涉及大量 double 类型数据操作和 \(\sin,\cos\) 运算,会产生误差.快速数论变换(Number Theoretic Transfo ...

  8. NTT 快速数论变换

    NTT 先学习FFT 由于FFT是使用复数运算,精度并不好,而且也无法取模,所以有了NTT(快速数论变换). 建议先完全理解FFT后再学习NTT. 原根 NTT使用与单位根性质相似的原根来代替单位根. ...

  9. 快速傅里叶变换 & 快速数论变换

    快速傅里叶变换 & 快速数论变换 [update 3.29.2017] 前言 2月10日初学,记得那时好像是正月十五放假那一天 当时写了手写版的笔记 过去近50天差不多忘光了,于是复习一下,具 ...

随机推荐

  1. stylus 使用小技巧(1)

    mixins.styl $pink_color = #fc566a $yellow_color = #fee246 add(a) a = unit(a, rem) pic_position($widt ...

  2. java输入一个整数N,打印1~n位数

    举个栗子:输入 3 : 打印1,2,3......999 这里要注意一个坑,不可以直接算出最大的数,然后从1开始打印 .因为当n足够大时,n位数必定会超出int范围和long范围 所以我们需要用字符串 ...

  3. 关于C#中Convert.ToInt32()是干什么用的

    并非每个对象都可以直接转换为int.例如,以下内容将无法编译: string Maomao = "100"; int i = (int)Maomao; 因为string无法隐式转换 ...

  4. rabbitmq使用延迟时报异常

    声明交换机为延迟时报异常( unknown exchange type 'x-delayed-message')的解决方法 服务端需下载安装插件: 1.下载插件包 2.将下载后的插件包移至服务安装目录 ...

  5. nc临时开启端口并监听

    port="6379 3306 27017 4505 4506 24007 24008 49152" #while true #do for i in $port do isexi ...

  6. 使用raise语句抛出异常

    #_author:来童星#date:2019/12/18def division(): num1=int(input('请输入被除数:')) num2=int(input('请输入除数:')) if ...

  7. 贪心——cf708b

    先求0,1的个数,然后贪心输出01即可 #include<bits/stdc++.h> using namespace std; #define ll long long ll a,b,c ...

  8. 使用HBuilder编辑器进行真机调试运行时提示Waiting for debugger!

    在使用HBuilder编辑器创建mui项目进行真机调试的时候,手机总是提示Waiting for debugger! 现在终于找到了解决办法: 手机 设置 -> 开发人员选项 -> USB ...

  9. Delphi 虚拟桌面

    Delphi创建虚拟桌面实现后台调用外部程序 核心提示:最近在做的一个软件,其中有一部分功能需要调用其它的软件来完成,而那个软件只有可执行文件,根本没有源代码,幸好,我要做的事不难,只需要在我的程序启 ...

  10. delphi 文件操作(信息获取)

    delphi获取Exe文件版本信息的函数 Type TFileVersionInfo = Record FixedInfo:TVSFixedFileInfo; {版本信息} CompanyName:S ...