从理论上说,经过人们优化的FFT已经十分优秀,能够处理大部分的多项式乘法,但是有的时候仍然会出现下面的情况:

1)常数仍然比较大

2)在进行与整数有关的FFT时,发现得到的结果是一堆诡异的数,你需要不停的和精度搏斗

那么在这时,你就需要学会快速数论变换(NTT)

前置芝士

快速傅里叶变换

你可以上网百度,或者看我的博客

阶与原根

我们由欧拉定理可以知道,对于任意的正整数\(a、m\),如果满足\(gcd(a,m)=1\),就有\(a^{\varphi(m)}\equiv 1(mod\ m)\)

但我们发现,还有一些数满足\(a^p\equiv 1(mod\ m)\)且\(p< \varphi(m)\),因此人们定义了阶

设\(m>1\),且\(gcd(a,m)=1\),则满足\(a^p\equiv 1(mod\ m)\)的最小正整数\(p\)成为\(a\)对模\(m\)的阶,记作\(\delta_m(a)\)

于是就会有\(\delta_m(a)|p\),充分性很显然,我们证一下必要性

我们设\(p=\delta_m(a)·q+r\)(其中\(0\leq r < \delta_m(a)\))

那么\(a^p=a^{\delta_m(a)·q}·a^r\equiv a^r\equiv 1(mod m)\)

由\(p\)是最小正整数知\(r=0\),所以\(\delta_m(a)|p\)

然后你就有了\(\delta_m(a)|\varphi(p)\)

好吧这个性质并没有什么用

由上面的欧拉定理,我们不难理解数学家们为什么搞出这么一个蛋疼的定义——原根

如果\(\delta_m(a)=\varphi(m)\),那么称\(a\)是模\(m\)的一个原根,为了下面表述的方便我们将它记作\(g\)

我们发现原根有一个这样的性质:\(g^0,g^1,g^2,\cdots,g^{\varphi(m)-1}\)构成了一个模\(m\)的完全剩余系

证明:考虑反证法,即假设c存在\(i,j\)(\(i>j\))满足\(g^i\equiv g^j(mod\ m)\)

​ 两边同时除以\(g^j\),有\(g^{i-j}\equiv 1\),而很明显\(i-j<\varphi(m)\),这与原根的定义相矛盾

​ 于是性质得证

性质验证

在FFT中,我们使用单位根的原因就是单位根满足的一些性质可以加速计算,如果原根也满足的话,那么我们在计算时可以直接替换

性质1

\(w_n^0,w_n^1,w_n^2,\cdots,w_n^{n-1}\)两两不同

这在上面已经得到了证明

性质2

\(w_{2n}^{2p}=w_n^p\)

如果\(w_{n}=g^p\),那么就应该有\(w_{2n}=g^{\frac{p}{2}}\),他们在乘上两倍的次幂之后值相等

性质3

\(w_{n}^{\frac{n}{2}+p}=-w_n^p\)

因为有\((g^{\frac{n}{2}})^2\equiv 1\),为了保持原根的定义,就会有\(g^{\frac{n}{2}}\equiv -1(mod \ n)\)

带回去运算即可

综上所述,原根满足原来的单位根所具有的性质,因此我们可以考虑用原根来代替单位根

实际运用

一点注意事项

1、原根的话在模数不确定的情况下需要自己求,不过如果模数是\(998244353\)或者\(1004535809\)的话,它们的原根是3

2、注意在IDFT的时候,原来直接除以的地方要换做求逆元

代码

#include<iostream>
#include<string>
#include<string.h>
#include<stdio.h>
#include<algorithm>
#include<math.h>
#include<vector>
#include<queue>
#include<map>
using namespace std;
#define rep(i,a,b) for (i=a;i<=b;i++)
typedef long long ll;
#define maxd 998244353
const double pi=acos(-1.0);
#define int long long
ll n,m,a[5005000],b[5005000];
int lim=1,r[5005000]; int qpow(int x,int y)
{
int ans=1,sum=x;
while (y)
{
int tmp=y%2;y/=2;
if (tmp) ans=(1ll*ans*sum)%maxd;
sum=(1ll*sum*sum)%maxd;
}
return ans;
} void ntt(int lim,ll *a,int typ)
{
int i;
for (i=0;i<lim;i++)
if (i<r[i]) swap(a[i],a[r[i]]);
int mid;
for (mid=1;mid<lim;mid<<=1)
{
int gn=qpow(3,(maxd-1)/(mid<<1));
int sta,len=mid<<1,j;
for (sta=0;sta<lim;sta+=len)
{
int g=1;
for (j=0;j<mid;j++,g=(g*gn)%maxd)
{
int x1=a[j+sta],y1=(g*a[j+sta+mid])%maxd;
a[j+sta]=(x1+y1)%maxd;
a[j+sta+mid]=(x1-y1+maxd)%maxd;
}
}
}
if (typ==-1) reverse(&a[1],&a[lim]);
} int read()
{
int x=0,f=1;char ch=getchar();
while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
return x*f;
} signed main()
{
n=read();m=read();int i,cnt=0;
for (i=0;i<=n;i++) a[i]=read();
for (i=0;i<=m;i++) b[i]=read();
while (lim<=n+m) {lim<<=1;cnt++;}
for (i=0;i<=lim;i++)
r[i]=((r[i>>1]>>1)|((i&1)<<(cnt-1)));
ntt(lim,a,1);
ntt(lim,b,1);
for (i=0;i<=lim;i++) a[i]=(a[i]*b[i])%maxd;
ntt(lim,a,-1);
int tmp=qpow(lim,maxd-2);
for (i=0;i<=n+m;i++)
{
a[i]=(a[i]*tmp)%maxd;
printf("%lld ",a[i]);
}
return 0;
}

NTT算法小结的更多相关文章

  1. C#排序算法小结

    前言 算法这个东西其实在开发中很少用到,特别是web开发中,但是算法也很重要,因为任何的程序,任何的软件,都是由很多的算法和数据结构组成的.但是这不意味着算法对于每个软件设计人员的实际工作都是很重要的 ...

  2. [图论]Dijkstra 算法小结

    Dijkstra 算法小结  By Wine93 2013.11 1. Dijkstra 算法相关介绍 算法阐述:Dijkstra是解决单源最短路径的算法,它可以在O(n^2)内计算出源点(s)到图中 ...

  3. [图论]Floyd 算法小结

    Floyd 算法小结  By Wine93 2013.11 1. Floyd算法简介 Floyd算法利用动态规划思想可以求出任意2点间的最短路径,时间复杂度为O(n^3),对于稠密图, 效率要高于执行 ...

  4. Paxos算法小结

    转自不正直的绅士,因百度空间迁移,无法注明出处,我从其google搜索引擎中的cache进行的copy. 不正直的绅士 是跟我一起工作过的非常有才的一个青年才俊. Paxos的使用非常广泛.sanlo ...

  5. 剑指Offer--排序算法小结

    剑指Offer--排序算法小结 前言 毕业季转眼即到,工作成为毕业季的头等大事,必须得认认真真进行知识储备,迎战笔试.电面.面试. 许久未接触排序算法了.平时偶尔接触到时自己会不假思索的百度,然后就是 ...

  6. 【路飞学城Day170】算法小结

    Evernote Export 算法的思想是能省则省,内存能少则少,时间运行能少尽量少 堆排序的时间复杂度O(nlogn) 堆排序的内置模块heapq 常用函数 heapify(x) heappush ...

  7. 原根求解算法 && NTT算法

    原根求解算法: 获取一个数\(N\)的原根\(root\)的算法 #include<bits/stdc++.h> #define ll long long #define IL inlin ...

  8. 贝叶斯个性化排序(BPR)算法小结

    在矩阵分解在协同过滤推荐算法中的应用中,我们讨论过像funkSVD之类的矩阵分解方法如何用于推荐.今天我们讲另一种在实际产品中用的比较多的推荐算法:贝叶斯个性化排序(Bayesian Personal ...

  9. 逻辑回归(Logistic Regression)算法小结

    一.逻辑回归简述: 回顾线性回归算法,对于给定的一些n维特征(x1,x2,x3,......xn),我们想通过对这些特征进行加权求和汇总的方法来描绘出事物的最终运算结果.从而衍生出我们线性回归的计算公 ...

随机推荐

  1. Linux iostat 命令

    iostat 命令是 I/O statistics(输入/输出统计)的缩写,用来报告系统的 CPU 统计信息和块设备及其分区的 IO 统计信息.iostat 是 sysstat 工具集的一个工具,在 ...

  2. Leetcode 中Linked List Cycle 一类问题

    141. Linked List Cycle Given a linked list, determine if it has a cycle in it. Follow up:Can you sol ...

  3. H5 69-清除浮动方式四

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  4. CSS scroll-behavior属性: 滚动框指定滚动行为

    概念 当用户手动导航或者 CSSOM scrolling API 触发滚动操作时,CSS 属性 scroll-behavior 为一个滚动框指定滚动行为,其他任何的滚动,例如那些由于用户行为而产生的滚 ...

  5. Mysql数据库触发器调用脚本

    一.数据库触发器 mysql触发器trigger 实例详解 对数据库触发器new和old的理解 示例 二.UDF mySql的UDF是什么 三.安装执行命令UDF mysql触发器调用外部脚本(安装) ...

  6. OSS网页上传和断点续传(STSToken篇)

    云账号AccessKey拥有所有API访问权限,在客户端不要直接使用,会泄露ak信息,造成安全问题.所以使用STS方式(临时账号权限)给客户端授权. C#版获取STSToken 一.下载阿里SDK(a ...

  7. 钢琴培训班课程、课时及费用管理系统已提供ACM3.0新版下载

    中小型艺术培训班课程.课时及费用管理系统. 2014新版 ACM3测试版下载:http://www.cnblogs.com/Charltsing/p/ACM3.html 您有任何功能需求,欢迎QQ 5 ...

  8. Elasticsearch之配置详解

    Cluster 集群名称,默认为elasticsearch: cluster.name: elasticsearch 设置一个节点的并发数量,有两种情况,一种是在初始复苏过程中: cluster.ro ...

  9. 在Git中添加一个项目

    首先保证Git服务器正确配置,管理员机器可正常连接并使用Git. 第一步:在服务器上新建一个项目仓库 切换到git用户: a@ubuntu:/home/git$ su - git $ cd /home ...

  10. Python_函数的有用信息、带参数的装饰器、多个装饰器装饰一个函数

    函数的有用信息 代码1: def login(username, password): """ 此函数需要用户名,密码两个参数,完成的是登录的功能. :return: T ...