MTT:任意模数NTT

概述

有时我们用FFT处理的数据很大,而模数可以分解为\(a\cdot 2^k+1\)的形式。次数用FFT精度不够,用NTT又找不到足够大的模数,于是MTT就应运而生了。

MTT没有模数的限制,比NTT更加自由,应用广泛,可以用于任意模数或很大的数。

MTT

MTT是基于NTT的,其思想很简单,就是做多次NTT,每次使用不同的素数,然后使用CRT合并解,在合并的过程中模最终模数,或是对于无模数的情况使用高精度。

做NTT的次数取决于最大可能答案的大小,所用的所有素数之积必须大于答案

实现

此处以取三个素数为例

我们可以做三次NTT,相邻次之间改变素数,但这样常数太大,于是我们常常选择封装(适合于模数不太多的情况)。

我们定义一个结构体node,有三个成员a,b,c,分别代表三个模数下的值,同时,我们定义模数的结构体与之一一对应。

struct node{

LL a,b,c;

node(){

a=b=c=0;

}

node(LL x){

a=b=c=x;

}

node(LL x,LL y,LL z){

a=x;

b=y;

c=z;

}

}MOD=node(167772161,469762049,998244353),BASE=node(3),INV=node(116878283,426037461,929031873);

我们还要定义关于此结构体的运算,其中成员之间互不影响,只和操作对象里对应的成员产生运算

inline node operator+(node x,node y){

return node(x.a+y.a,x.b+y.b,x.c+y.c);

}

inline node operator-(node x,node y){

return node(x.a-y.a,x.b-y.b,x.c-y.c);

}

inline node operator*(node x,node y){

return node(x.a*y.a%MOD.a,x.b*y.b%MOD.b,x.c*y.c%MOD.c);

}

inline node operator%(node x,node y){

return node(x.a%y.a,x.b%y.b,x.c%y.c);

}

inline node operator/(node x,node y){

return node(x.a/y.a,x.b/y.b,x.c/y.c);

}

inline node operator-(node x,LL y){

return node(x.a-y,x.b-y,x.c-y);

}

inline node operator*(node x,LL y){

return node(x.a*y,x.b*y,x.c*y);

}

inline node operator/(node x,LL y){

return node(x.a/y,x.b/y,x.c/y);

}

inline node operator%(node x,LL y){

return node(x.a%y,x.b%y,x.c%y);

}

然后套用NTT的板子,最后用CRT合并。

假设这一位的答案是\(x\),三个模数分别为\(A,B,C\),那么:

\[\begin{aligned}x\equiv x_1\pmod{A} \\ x\equiv x_2\pmod{B} \\ x\equiv x_3\pmod{C}\end{aligned}

\]

先把前两个合并:

\[\begin{aligned}x_1+k_1A=x_2+k_2B\\x_1+k_1A\equiv x_2\pmod{B}\\k_1\equiv \frac{x_2-x_1}A\pmod{B}\end{aligned}

\]

于是求出了\(k_1\),也就求出了\(x\equiv x_1+k_1A\pmod{AB}\),记\(x_4=x_1+k_1A\)

\[\begin{aligned}x_4+k_4AB=x_3+k_3C\\x_4+k_4AB\equiv x_3\pmod{C}\\k_4\equiv \dfrac{x_3-x_4}{AB}\pmod{C}\end{aligned}

\]

因为\(x

\[x=x_4+k_4AB

\]

LL CRT(node x){

LL mod1=MOD.a,mod2=MOD.b,mod3=MOD.c,mod_1_2=mod1*mod2;

LL inv_1=inv(mod1,mod2),inv_2=inv(mod1*mod2%mod3,mod3);

LL A=x.a,B=x.b,C=x.c;

LL x4=(B-A+mod2)%mod2*inv_1%mod2*mod1+A;

return ((C-x4%mod3+mod3)%mod3*inv_2%mod3*(mod_1_2%Last_Mod)+Last_Mod+x4)%Last_Mod;

}

于是我们就能写出完整代码了。

// luogu-judger-enable-o2

#include

using namespace std;

typedef long long LL;

const int INF=1e9+7,MAXN=3e6+10/*Min:2^20+10*/;

void exgcd(LL a,LL b,LL &x,LL &y){

if(!b){

x=1;

y=0;

return;

}

exgcd(b,a%b,y,x);

y-=a/b*x;

}

inline LL inv(LL x,LL p){

LL a,b;

exgcd(x,p,a,b);

return (a%p+p)%p;

}

struct node{

LL a,b,c;

node(){

a=b=c=0;

}

node(LL x){

a=b=c=x;

}

node(LL x,LL y,LL z){

a=x;

b=y;

c=z;

}

}MOD=node(167772161,469762049,998244353),BASE=node(3),INV=node(116878283,426037461,929031873);

inline node operator+(node x,node y){

return node(x.a+y.a,x.b+y.b,x.c+y.c);

}

inline node operator-(node x,node y){

return node(x.a-y.a,x.b-y.b,x.c-y.c);

}

inline node operator*(node x,node y){

return node(x.a*y.a%MOD.a,x.b*y.b%MOD.b,x.c*y.c%MOD.c);

}

inline node operator%(node x,node y){

return node(x.a%y.a,x.b%y.b,x.c%y.c);

}

inline node operator/(node x,node y){

return node(x.a/y.a,x.b/y.b,x.c/y.c);

}

inline node operator-(node x,LL y){

return node(x.a-y,x.b-y,x.c-y);

}

inline node operator*(node x,LL y){

return node(x.a*y,x.b*y,x.c*y);

}

inline node operator/(node x,LL y){

return node(x.a/y,x.b/y,x.c/y);

}

inline node operator%(node x,LL y){

return node(x.a%y,x.b%y,x.c%y);

}

LL Last_Mod;

LL CRT(node x){

LL mod1=MOD.a,mod2=MOD.b,mod3=MOD.c,mod_1_2=mod1*mod2;

LL inv_1=inv(mod1,mod2),inv_2=inv(mod1*mod2%mod3,mod3);

LL A=x.a,B=x.b,C=x.c;

LL x4=(B-A+mod2)%mod2*inv_1%mod2*mod1+A;

return ((C-x4%mod3+mod3)%mod3*inv_2%mod3*(mod_1_2%Last_Mod)+Last_Mod+x4)%Last_Mod;

}

inline LL fpm_(LL base,LL p,LL mod){

LL ret=1;

while(p){

if(p&1)

ret=ret*base%mod;

base=base*base%mod;

p>>=1;

}

return ret%mod;

}

inline node fpm(LL base,node p){

return node(fpm_(base,p.a,MOD.a),fpm_(base,p.b,MOD.b),fpm_(base,p.c,MOD.c));

}

int N,M,lim=1,lg,rev[MAXN];

node Wn[MAXN];

inline void NTT(node *a,int type){

for(int i=0;i

if(i

swap(a[i],a[rev[i]]);

for(int mid=1;mid

int len=mid<<1/*n*/;

node Wn=fpm(3,(MOD-1)/(LL)len);

for(int j=0;j

node w=node(1);

for(int k=0;k

node 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;

}

}

}

if(type==-1){

reverse(a+1,a+lim);

node lim_inv=node(inv(lim,MOD.a),inv(lim,MOD.b),inv(lim,MOD.c));

for(int i=0;i

a[i]=a[i]*lim_inv;

}

}

node a[MAXN],b[MAXN];

int main(){

scanf("%d%d%lld",&N,&M,&Last_Mod);

for(int i=0;i<=N;i++){

LL ii;

scanf("%lld",&ii);

a[i]=node(ii%Last_Mod)%MOD;

}

for(int i=0;i<=M;i++){

LL ii;

scanf("%lld",&ii);

b[i]=node(ii%Last_Mod)%MOD;

}

while(lim<=N+M){

lim<<=1;

lg++;

}

for(int i=0;i

rev[i]=(rev[i>>1]>>1)|((i&1)<

Wn[0]=node(1);

for(int i=1;i

Wn[i]=Wn[i-1]*INV;

NTT(a,1);

NTT(b,1);

for(int i=0;i

a[i]=a[i]*b[i]%MOD;

NTT(a,-1);

for(int i=0;i<=N+M;i++)

printf("%lld ",CRT(a[i]));

return 0;

}

例题

任意模数ntt_MTT:任意模数NTT相关推荐

  1. mac邮件过滤器SpamSieve,支持任意类型的任意数量的电子邮件帐户

    SpamSieve for mac是适用于Mac系统的一款邮件过滤器.spamsieve mac支持任意类型的任意数量的电子邮件帐户,可以通过各种标准可靠地识别哪些消息不是垃圾邮件,了解您的合法邮件是 ...

  2. 【JS】教你如何在对象内任意位置插入任意属性

    [JS]教你如何在对象内任意位置插入任意属性 如果你不知道我写这篇文章的目的是什么,那不妨想想这样一个场景 var obj = {a: "A",c: "C",d ...

  3. 利用Matlab将任意曲线旋转任意角度

    利用Matlab将任意曲线旋转任意角度 公式计算 数据 matlab代码 结果 公式计算 对于如上图所示的任意曲线,需要绕固定点O旋转某一角度,取曲线上任意一点A计算旋转后的坐标值. 设OA的长度为L ...

  4. 比大小,人类智慧天花板,任意类型,任意个数。内容包含函数模板的创建,类的创建,动态内存的分配与释放,函数调用指针的用法。牵扯多个知识点。

    比大小,看这一篇就足够,大家好,我是姜姜一名热爱C++编程的大学生,接下来我将通过代码演示如何利用C++实现任意类型,任意个数的比大小,并且找出最大的数. 由于本人比较懒,所有没写注释还请各位读者多多 ...

  5. Android设置SurfaceView任意大小、任意位置、保持预览宽高比与屏显一致

    一.任意大小.任意位置 1) 代码 public void init() {// FrameLayoutViewGroup.LayoutParams framelayout_params =new V ...

  6. 内核提权,任意地址写任意数据/固定数据模型

    实验环境 xp sp3 此实验将一个不常用的内核函数置0,然后R3申请了0地址的指针,将shellcode拷到此内存,内核并没有做ProbeForRead /Write检查 直接对传入的数据进行了修改 ...

  7. android surfaceview 大小,Android设置SurfaceView任意大小、任意位置、保持预览宽高比与屏...

    Android设置SurfaceView任意大小.任意位置.保持预览宽高比与屏 Android设置SurfaceView任意大小.任意位置.保持预览宽高比与屏显一致 一.任意大小.任意位置 1) 代码 ...

  8. shell文件通配符(9):任意一个:?、任意数量:*、任意包含[]、[^]:任意不包含

    文章目录 一. 有哪些文件通配符 二. 匹配任意 ?:匹配任意一个字符 *:匹配任意数量的字符串 三. 匹配任意指定一个字符 1. []:匹配任意包含 匹配任意包含 字符范围 2. [!]与[^]:匹 ...

  9. 任意模数ntt_任意模数NTT

    任意模数\(NTT\) 众所周知,为了满足单位根的性质,\(NTT\)需要质数模数,而且需要能写成\(a2^{k} + 1\)且\(2^k \ge n\) 比较常用的有\(998244353,1004 ...

最新文章

  1. QQ超时不能刷新好友接收发送信息
  2. 物体分割--Deep Watershed Transform for Instance Segmentation
  3. Java基础系列--Executor框架(一)
  4. 国内外交互体验很好的十款验证码
  5. .以及JDK1.5ConcurrentHashMap新特性
  6. Linux 下查看某一个程序所使用的内存方法介绍
  7. qt中调整弹出框的位置
  8. Position independent code and data (ROPI and RWPI)
  9. java list 元素排序_对arraylist中元素进行排序实例代码
  10. light4java_Light Weight Component Library for Java
  11. 朝鲜国家黑客被指利用 LinkedIn 攻击欧洲航空公司和军队企业
  12. 程序员面试金典——2.4链表分割
  13. c语言指针详解参数,C语言之指针详解
  14. 小米4 win10 刷回android,小米4怎么从Win10刷回miui7
  15. python画二元一次函数图像_Python实现的拟合二元一次函数功能示例【基于scipy模块】...
  16. 使用 Golang 实现简易的令牌桶算法
  17. SIP协议详解(中文)-5
  18. 使用Python2.7批量查询云窗IP
  19. 分库分表就能无限扩容吗
  20. 外包 | LBP/HOG/CNN 实现对 CK/jaffe/fer2013 人脸表情数据集分类

热门文章

  1. Dev-cpp自定义主题:
  2. Markdown中常用LaTex数学符号和数学公式排版整理
  3. Hack The Box--Forest 靶场训练
  4. php 中字符串长度不正确
  5. 把opencv Mat 按位存成bmp二值图像 (1bit 1pixel)
  6. pygame之窗口大小调整
  7. codewars解题笔记---Are You Playing Banjo?
  8. matlab 替换字符 数字,使用正则表达式替换文本
  9. Lumerical---FDE中的金属边界条件仿真
  10. 转载:电脑cmd命令怎么测试网速详细步骤