【模板篇】NTT和三模数NTT
之前写过FFT的笔记. 我们知道FFT是在复数域上进行的变换.
而且经过数学家的证明, DFT是复数域上唯一满足循环卷积性质的变换.
而我们在OI中, 经常遇到对xxxx取模的题目, 这就启发我们可不可以在模运算的意义下找一个这样的变换.
然后我们发现有个神奇的东西, 原根\(g\), 这东西在模意义下相当于单位复根\(-e^{\frac{2\pi i}{n}}\).
所以我们预处理一下\(g\)的幂和逆元, 然后改一下fft的代码就出现了快速数论变换ntt
懒得写了 直接上代码:
void getwn(){ //预处理原根的幂和逆元
int x=qpow(3,p-2);
for(int i=0;i<20;++i){
wn[i]=qpow(3,(p-1)/(1<<i));
inv[i]=qpow(x,(p-1)/(1<<i));
}
}
void ntt(int *y,bool f){ rev(y); //翻转代码和fft无异
for(int m=2,id=1;m<=n;m<<=1,++id){ //id用来记录转到第几下了
for(int k=0;k<n;k+=m){
int w=1,wm=f?wn[id]:inv[id]; //如果是dft就用幂, idft就用幂的逆元
for(int j=0;j<m/2;++j){
//这里跟fft一样, 不过要对p取模
int u=y[k+j]%p,t=1ll*w*y[k+j+m/2]%p;
y[k+j]=u+t; if(y[k+j]>p) y[k+j]-=p;
y[k+j+m/2]=u-t; if(y[k+j+m/2]<0) y[k+j+m/2]+=p;
w=1ll*w*wm%p;
}
}
}
if(!f){
int x=qpow(n,p-2);
for(int i=0;i<n;++i)
y[i]=1ll*y[i]*x%p;
}
}
好像差不多呢~ 不过这样就要求我们找一个原根好求的数. 比如著名的uoj数: 998244353 还有1004535809和469762049等, 这三个数原根都是3~
好像因为当时一看到模数不是1e9+7一般就会想到ntt, vfk为了防止这一点, 模数统一采用998244353, 现在看看收效不错.
不过 有些丧心病狂的人就是要用1e9+7作为ntt的模数, 甚至还出现了可以不模质数的情况!
那我们怎么解决任意模数ntt呢? 我们可以采用拆系数ntt或者三模数ntt. 这里介绍一下三模数ntt.
对于一般的数据范围, \(n\leq10^5, a_i\leq10^9\), 这样可能会到\(10^5*10^{9^2}=10^{23}\)级别.
所以我们可以找三个乘积\(>10^{23}\)的ntt-friendly的数, 然后分别ntt再想办法合并.
我们假如答案是ans, 那我们做三次ntt后就能得到如下三个柿子.
ans\equiv a_1(\mod m_1)\\
ans\equiv a_2(\mod m_2)\\
ans\equiv a_3(\mod m_3)
\end{matrix}\right.
\]
我们把前两个柿子通过中国剩余定理合并, 就可以得到
ans\equiv A(\mod M)\\
ans\equiv a_3(\mod m_3)
\end{matrix}\right.
\]
其中, \(M=m_1*m_2\)
这样我们设\(ans=kM+A\),
k=(a_3-A)*M^{-1} (\mod m_3)
\]
这样我们求出\(k\)然后代回到\(ans=kM+A\)就可以求对任意模数取模的结果了.
中国剩余定理合并的时候直接乘是可以爆long long的, 所以我们要用到\(O(1)\)快速乘~
下面上一波代码: luogu4245 【模板】MTT
哎呀觉得自己码风有点丑啊qwq
#include <cstdio>
#include <cstring>
#include <algorithm>
typedef long long LL;
const int N=600020,p0=469762049,p1=998244353,p2=1004535809;
const LL M=1ll*p0*p1;
int wn[20],nw[20],rev[N],n,lg,p;
int qpow(int a,int b,int p,int s=1){
for(;b;b>>=1,a=1ll*a*a%p)
if(b&1) s=1ll*s*a%p;
return s;
}
LL mul(LL a,LL b,LL p){ a%=p; b%=p;
return (a*b-(LL)((long double)a*b/p)*p+p)%p;
}
void calcw(int p){
int x=qpow(3,p-2,p);
for(int i=0;i<20;++i){
wn[i]=qpow(3,(p-1)/(1<<i),p);
nw[i]=qpow(x,(p-1)/(1<<i),p);
}
}
void init(){
for(int i=0;i<n;++i)
rev[i]=(rev[i>>1]>>1)|((i&1)<<lg);
}
void ntt(int *y,bool f,int p){ calcw(p);
for(int i=0;i<n;++i) if(i<rev[i]) std::swap(y[i],y[rev[i]]);
for(int m=2,id=1;m<=n;m<<=1,++id){
for(int k=0;k<n;k+=m){
int w=1,wm=f?wn[id]:nw[id];
for(int j=0;j<m>>1;++j){
int &a=y[k+j]; int &b=y[k+j+m/2];
int u=a%p,t=1ll*w*b%p;
a=u+t; if(a>p) a-=p;
b=u-t; if(b<0) b+=p;
w=1ll*w*wm%p;
}
}
} int x=qpow(n,p-2,p);
if(!f) for(int i=0;i<n;++i) y[i]=1ll*y[i]*x%p;
}
char c1[N],c2[N]; int a[N],b[N],c[N],d[N],ans[3][N];
int main(){
int l1,l2; scanf("%d%d%d",&l1,&l2,&p);
for(int i=0;i<=l1;++i) scanf("%d",&a[i]),a[i]%=p;
for(int i=0;i<=l2;++i) scanf("%d",&b[i]),b[i]%=p;
for(n=1;n<l1||n<l2;n<<=1,++lg); n<<=1; init();
std::copy(a,a+n,c); std::copy(b,b+n,d);
ntt(c,1,p0); ntt(d,1,p0);
for(int i=0;i<n;++i) ans[0][i]=1ll*c[i]*d[i]%p0;
std::copy(a,a+n,c); std::copy(b,b+n,d);
ntt(c,1,p1); ntt(d,1,p1);
for(int i=0;i<n;++i) ans[1][i]=1ll*c[i]*d[i]%p1;
std::copy(a,a+n,c); std::copy(b,b+n,d);
ntt(c,1,p2); ntt(d,1,p2);
for(int i=0;i<n;++i) ans[2][i]=1ll*c[i]*d[i]%p2;
ntt(ans[0],0,p0); ntt(ans[1],0,p1); ntt(ans[2],0,p2);
for(int i=0;i<n;++i){
LL A=mul(1ll*ans[0][i]*p1%M,qpow(p1%p0,p0-2,p0),M)
+mul(1ll*ans[1][i]*p0%M,qpow(p0%p1,p1-2,p1),M);
if(A>M) A-=M;
LL k=((ans[2][i]-A)%p2+p2)%p2*qpow(M%p2,p2-2,p2)%p2;
a[i]=1ll*(k%p)*(M%p)%p+A%p;
if(a[i]>p) a[i]-=p;
}
for(int i=0;i<=l1+l2;++i) printf("%d ",a[i]);
}
【模板篇】NTT和三模数NTT的更多相关文章
- 洛谷.4245.[模板]任意模数NTT(MTT/三模数NTT)
题目链接 三模数\(NTT\): 就是多模数\(NTT\)最后\(CRT\)一下...下面两篇讲的都挺明白的. https://blog.csdn.net/kscla/article/details/ ...
- 洛谷 P4245 [模板]任意模数NTT —— 三模数NTT / 拆系数FFT(MTT)
题目:https://www.luogu.org/problemnew/show/P4245 用三模数NTT做,需要注意时间和细节: 注意各种地方要取模!传入 upt() 里面的数一定要不超过2倍 m ...
- 洛谷 4245 【模板】任意模数NTT——三模数NTT / 拆系数FFT
题目:https://www.luogu.org/problemnew/show/P4245 三模数NTT: 大概是用3个模数分别做一遍,用中国剩余定理合并. 前两个合并起来变成一个 long lon ...
- 三模数NTT模板
求两个多项式的卷积对任意数p取模 两个好记的FNT模数: 5*2^25+1 7*2^26+1 原根都为3 //Achen #include<algorithm> #include<i ...
- 洛谷P4245 【模板】MTT(任意模数NTT)
题目背景 模板题,无背景 题目描述 给定 22 个多项式 F(x), G(x)F(x),G(x) ,请求出 F(x) * G(x)F(x)∗G(x) . 系数对 pp 取模,且不保证 pp 可以分解成 ...
- Luogu 4245 【模板】任意模数NTT
这个题还有一些其他的做法,以后再补,先记一下三模数$NTT$的方法. 发现这个题不取模最大的答案不会超过$10^5 \times 10^9 \times 10^9 = 10^{23}$,也就是说我们可 ...
- 【洛谷P4245】 【模板】任意模数NTT
三模数 NTT,感觉不是很难写 $?$ 代码借鉴的 https://www.cnblogs.com/Mychael/p/9297652.html code: #include <bits/std ...
- 再探快速傅里叶变换(FFT)学习笔记(其三)(循环卷积的Bluestein算法+分治FFT+FFT的优化+任意模数NTT)
再探快速傅里叶变换(FFT)学习笔记(其三)(循环卷积的Bluestein算法+分治FFT+FFT的优化+任意模数NTT) 目录 再探快速傅里叶变换(FFT)学习笔记(其三)(循环卷积的Blueste ...
- 【知识总结】多项式全家桶(三)(任意模数NTT)
经过两个月的咕咕,"多项式全家桶" 系列终于迎来了第三期--(雾) 上一篇:[知识总结]多项式全家桶(二)(ln和exp) 先膜拜(伏地膜)大恐龙的博客:任意模数 NTT (在页面 ...
随机推荐
- 迄今为止把同步/异步/阻塞/非阻塞/BIO/NIO/AIO讲的这么清楚的好文章(快快珍藏)
常规的误区 假设有一个展示用户详情的需求,分两步,先调用一个HTTP接口拿到详情数据,然后使用适合的视图展示详情数据. 如果网速很慢,代码发起一个HTTP请求后,就卡住不动了,直到十几秒后才拿到HTT ...
- 关于Django路由层简单笔记
Django—路由层 URL配置(URLconf)就像Django 所支撑网站的目录.它的本质是URL与要为该URL调用的视图函数之间的映射表:你就是以这种方式告诉Django,对于客户端发来的某个U ...
- 常用Concurrent.util包工具类——高并发
一 Concurrent.util常用类: 1. CyclicBarrier: 假设有场景:每个线程代表一个跑步运动员,当运动员都准备好后,才一起出发只要有一个人没有准备好,大家都等待. import ...
- HumanNet v2:用于疾病研究的人类基因网络 X科研网
HumanNet v2:用于疾病研究的人类基因网络人类基因网络已被证明在疾病研究的许多方面都很有用,已经开发了许多基于网络的策略来产生关于基因 - 疾病 - 药物关联的假设.预测和组织与特定疾病最相关 ...
- FastDFSClient上传图片工具类
package cn.lijun.core.util; import org.apache.commons.io.FilenameUtils;import org.csource.common.Nam ...
- Linux Bash Shell快速入门 (二)
BASH 中的变量介绍BASH 中的变量都是不能含有保留字,不能含有 "-" 等保留字符,也不能含有空格. 简单变量在 BASH 中变量定义是不需要的,没有 "int i ...
- http协议和file协议的区别
1.在本地直接使用浏览器打开 html文件 和 通过本地服务器打开 html文件 有什么区别呢. https://segmentfault.com/q/1010000006554881/a-1 ...
- 【从0到1,搭建Spring Boot+RESTful API+Shiro+Mybatis+SQLServer权限系统】01、环境准备
开发环境 windows+STS(一个针对Spring优化的Eclipse版本)+Maven+SQLServer 环境部署 1.安装SQLServer(使用版本2008R2) 自行安装,此处略过 2. ...
- java中常用的转义字符(转)
Java编程中往往需要一些特殊操作,例如空格,换行.或者一些你使用特殊符号的意愿与程序中特殊符号意思冲突的时候,我们不能直接写就需要把这些符号转义,表达你的本意,并与程序中特殊符号做区分,这些都需要转 ...
- 架构师技能树skill-map
# 架构师技能树 ## 系统架构能力 ### 基本理论- 扩展性设计- 可用性设计- 可靠性设计- 一致性设计- 负载均衡设计- 过载保护设计 ### 协议设计- 二进制协议- 文本协议 ### 接入 ...