MTT:任意模数NTT

概述

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

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

MTT

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

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

实现

此处以取三个素数为例

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

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

  1. struct node{
  2. LL a,b,c;
  3. node(){
  4. a=b=c=0;
  5. }
  6. node(LL x){
  7. a=b=c=x;
  8. }
  9. node(LL x,LL y,LL z){
  10. a=x;
  11. b=y;
  12. c=z;
  13. }
  14. }MOD=node(167772161,469762049,998244353),BASE=node(3),INV=node(116878283,426037461,929031873);

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

  1. inline node operator+(node x,node y){
  2. return node(x.a+y.a,x.b+y.b,x.c+y.c);
  3. }
  4. inline node operator-(node x,node y){
  5. return node(x.a-y.a,x.b-y.b,x.c-y.c);
  6. }
  7. inline node operator*(node x,node y){
  8. return node(x.a*y.a%MOD.a,x.b*y.b%MOD.b,x.c*y.c%MOD.c);
  9. }
  10. inline node operator%(node x,node y){
  11. return node(x.a%y.a,x.b%y.b,x.c%y.c);
  12. }
  13. inline node operator/(node x,node y){
  14. return node(x.a/y.a,x.b/y.b,x.c/y.c);
  15. }
  16. inline node operator-(node x,LL y){
  17. return node(x.a-y,x.b-y,x.c-y);
  18. }
  19. inline node operator*(node x,LL y){
  20. return node(x.a*y,x.b*y,x.c*y);
  21. }
  22. inline node operator/(node x,LL y){
  23. return node(x.a/y,x.b/y,x.c/y);
  24. }
  25. inline node operator%(node x,LL y){
  26. return node(x.a%y,x.b%y,x.c%y);
  27. }

然后套用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<ABC\),所以

\[x=x_4+k_4AB
\]

  1. LL CRT(node x){
  2. LL mod1=MOD.a,mod2=MOD.b,mod3=MOD.c,mod_1_2=mod1*mod2;
  3. LL inv_1=inv(mod1,mod2),inv_2=inv(mod1*mod2%mod3,mod3);
  4. LL A=x.a,B=x.b,C=x.c;
  5. LL x4=(B-A+mod2)%mod2*inv_1%mod2*mod1+A;
  6. return ((C-x4%mod3+mod3)%mod3*inv_2%mod3*(mod_1_2%Last_Mod)+Last_Mod+x4)%Last_Mod;
  7. }

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

  1. // luogu-judger-enable-o2
  2. #include<bits/stdc++.h>
  3. using namespace std;
  4. typedef long long LL;
  5. const int INF=1e9+7,MAXN=3e6+10/*Min:2^20+10*/;
  6. void exgcd(LL a,LL b,LL &x,LL &y){
  7. if(!b){
  8. x=1;
  9. y=0;
  10. return;
  11. }
  12. exgcd(b,a%b,y,x);
  13. y-=a/b*x;
  14. }
  15. inline LL inv(LL x,LL p){
  16. LL a,b;
  17. exgcd(x,p,a,b);
  18. return (a%p+p)%p;
  19. }
  20. struct node{
  21. LL a,b,c;
  22. node(){
  23. a=b=c=0;
  24. }
  25. node(LL x){
  26. a=b=c=x;
  27. }
  28. node(LL x,LL y,LL z){
  29. a=x;
  30. b=y;
  31. c=z;
  32. }
  33. }MOD=node(167772161,469762049,998244353),BASE=node(3),INV=node(116878283,426037461,929031873);
  34. inline node operator+(node x,node y){
  35. return node(x.a+y.a,x.b+y.b,x.c+y.c);
  36. }
  37. inline node operator-(node x,node y){
  38. return node(x.a-y.a,x.b-y.b,x.c-y.c);
  39. }
  40. inline node operator*(node x,node y){
  41. return node(x.a*y.a%MOD.a,x.b*y.b%MOD.b,x.c*y.c%MOD.c);
  42. }
  43. inline node operator%(node x,node y){
  44. return node(x.a%y.a,x.b%y.b,x.c%y.c);
  45. }
  46. inline node operator/(node x,node y){
  47. return node(x.a/y.a,x.b/y.b,x.c/y.c);
  48. }
  49. inline node operator-(node x,LL y){
  50. return node(x.a-y,x.b-y,x.c-y);
  51. }
  52. inline node operator*(node x,LL y){
  53. return node(x.a*y,x.b*y,x.c*y);
  54. }
  55. inline node operator/(node x,LL y){
  56. return node(x.a/y,x.b/y,x.c/y);
  57. }
  58. inline node operator%(node x,LL y){
  59. return node(x.a%y,x.b%y,x.c%y);
  60. }
  61. LL Last_Mod;
  62. LL CRT(node x){
  63. LL mod1=MOD.a,mod2=MOD.b,mod3=MOD.c,mod_1_2=mod1*mod2;
  64. LL inv_1=inv(mod1,mod2),inv_2=inv(mod1*mod2%mod3,mod3);
  65. LL A=x.a,B=x.b,C=x.c;
  66. LL x4=(B-A+mod2)%mod2*inv_1%mod2*mod1+A;
  67. return ((C-x4%mod3+mod3)%mod3*inv_2%mod3*(mod_1_2%Last_Mod)+Last_Mod+x4)%Last_Mod;
  68. }
  69. inline LL fpm_(LL base,LL p,LL mod){
  70. LL ret=1;
  71. while(p){
  72. if(p&1)
  73. ret=ret*base%mod;
  74. base=base*base%mod;
  75. p>>=1;
  76. }
  77. return ret%mod;
  78. }
  79. inline node fpm(LL base,node p){
  80. return node(fpm_(base,p.a,MOD.a),fpm_(base,p.b,MOD.b),fpm_(base,p.c,MOD.c));
  81. }
  82. int N,M,lim=1,lg,rev[MAXN];
  83. node Wn[MAXN];
  84. inline void NTT(node *a,int type){
  85. for(int i=0;i<lim;i++)
  86. if(i<rev[i])
  87. swap(a[i],a[rev[i]]);
  88. for(int mid=1;mid<lim;mid<<=1){
  89. int len=mid<<1/*n*/;
  90. node Wn=fpm(3,(MOD-1)/(LL)len);
  91. for(int j=0;j<lim;j+=len){
  92. node w=node(1);
  93. for(int k=0;k<mid;k++){
  94. node x=a[j+k],y=w*a[j+k+mid]%MOD;
  95. a[j+k]=(x+y)%MOD;
  96. a[j+k+mid]=(x-y+MOD)%MOD;
  97. w=w*Wn%MOD;
  98. }
  99. }
  100. }
  101. if(type==-1){
  102. reverse(a+1,a+lim);
  103. node lim_inv=node(inv(lim,MOD.a),inv(lim,MOD.b),inv(lim,MOD.c));
  104. for(int i=0;i<lim;i++)
  105. a[i]=a[i]*lim_inv;
  106. }
  107. }
  108. node a[MAXN],b[MAXN];
  109. int main(){
  110. scanf("%d%d%lld",&N,&M,&Last_Mod);
  111. for(int i=0;i<=N;i++){
  112. LL ii;
  113. scanf("%lld",&ii);
  114. a[i]=node(ii%Last_Mod)%MOD;
  115. }
  116. for(int i=0;i<=M;i++){
  117. LL ii;
  118. scanf("%lld",&ii);
  119. b[i]=node(ii%Last_Mod)%MOD;
  120. }
  121. while(lim<=N+M){
  122. lim<<=1;
  123. lg++;
  124. }
  125. for(int i=0;i<lim;i++)
  126. rev[i]=(rev[i>>1]>>1)|((i&1)<<(lg-1));
  127. Wn[0]=node(1);
  128. for(int i=1;i<lim;i++)
  129. Wn[i]=Wn[i-1]*INV;
  130. NTT(a,1);
  131. NTT(b,1);
  132. for(int i=0;i<lim;i++)
  133. a[i]=a[i]*b[i]%MOD;
  134. NTT(a,-1);
  135. for(int i=0;i<=N+M;i++)
  136. printf("%lld ",CRT(a[i]));
  137. return 0;
  138. }

例题

模板题:P4245 【模板】任意模数NTT
模板题:【模板】多项式求逆(加强版)

MTT:任意模数NTT的更多相关文章

  1. 洛谷P4245 【模板】MTT(任意模数NTT)

    题目背景 模板题,无背景 题目描述 给定 22 个多项式 F(x), G(x)F(x),G(x) ,请求出 F(x) * G(x)F(x)∗G(x) . 系数对 pp 取模,且不保证 pp 可以分解成 ...

  2. 【模板】任意模数NTT

    题目描述: luogu 题解: 用$fft$水过(什么$ntt$我不知道). 众所周知,$fft$精度低,$ntt$处理范围小. 所以就有了任意模数ntt神奇$fft$! 意思是这样的.比如我要算$F ...

  3. 任意模数NTT

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

  4. BZOJ1042 HAOI2008硬币购物(任意模数NTT+多项式求逆+生成函数/容斥原理+动态规划)

    第一眼生成函数.四个等比数列形式的多项式相乘,可以化成四个分式.其中分母部分是固定的,可以多项式求逆预处理出来.而分子部分由于项数很少,询问时2^4算一下贡献就好了.这个思路比较直观.只是常数巨大,以 ...

  5. [洛谷P4245]【模板】任意模数NTT

    题目大意:给你两个多项式$f(x)$和$g(x)$以及一个模数$p(p\leqslant10^9)$,求$f*g\pmod p$ 题解:任意模数$NTT$,最大的数为$p^2\times\max\{n ...

  6. 【知识总结】多项式全家桶(三)(任意模数NTT)

    经过两个月的咕咕,"多项式全家桶" 系列终于迎来了第三期--(雾) 上一篇:[知识总结]多项式全家桶(二)(ln和exp) 先膜拜(伏地膜)大恐龙的博客:任意模数 NTT (在页面 ...

  7. 再探快速傅里叶变换(FFT)学习笔记(其三)(循环卷积的Bluestein算法+分治FFT+FFT的优化+任意模数NTT)

    再探快速傅里叶变换(FFT)学习笔记(其三)(循环卷积的Bluestein算法+分治FFT+FFT的优化+任意模数NTT) 目录 再探快速傅里叶变换(FFT)学习笔记(其三)(循环卷积的Blueste ...

  8. 洛谷.4245.[模板]任意模数NTT(MTT/三模数NTT)

    题目链接 三模数\(NTT\): 就是多模数\(NTT\)最后\(CRT\)一下...下面两篇讲的都挺明白的. https://blog.csdn.net/kscla/article/details/ ...

  9. 洛谷 P4245 [模板]任意模数NTT —— 三模数NTT / 拆系数FFT(MTT)

    题目:https://www.luogu.org/problemnew/show/P4245 用三模数NTT做,需要注意时间和细节: 注意各种地方要取模!传入 upt() 里面的数一定要不超过2倍 m ...

随机推荐

  1. 1.隐藏继承的成员new / 虚方法(override)/ abstract / 多态 ----- 重写

    总结: 1. 在继承上, new/override没区别 2. 在多态上,new不支持多态,override支持 在C#中改变类中相同名称的方法的实现过程中有三种方式:重载.重写和覆盖. 重载:指具有 ...

  2. 如何设置和使用MacOS上的Microsoft Office套件

    自30年前首次发布以来,Microsoft Office已成为全球最受欢迎的生产力套件之一.借助Word和Excel for Mac之类的程序,毫无疑问,MS Office套件在任何计算机上都是必须下 ...

  3. idea 增量包配置

    set CATALINA_OPTS=-server -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,serve ...

  4. 阿里云资深技术专家黄省江:让天下没有难做的SaaS

    导语:本文中,阿里云资深技术专家黄省江(花名禅笑)将聚焦“SaaS加速器——让天下没有难做的SaaS”,对伙伴来说,SaaS加速器帮助他们做好SaaS,卖好SaaS:对企业来说,SaaS加速器帮助他们 ...

  5. python语法学习

    global关键字(内部作用域想要对外部作用域的变量进行修改) decator装饰器,说白了就是一个函数指针的传递 *arg,**kwarg, 分别为tuple,dic传递

  6. (转)OpenFire源码学习之十五:插件开发

    转:http://blog.csdn.net/huwenfeng_2011/article/details/43418493 Plugin接口规范 插件是openfire功能的增强表现,它的主要任务: ...

  7. (转)NAT与NAT穿越学习总结--ICE过程讲的不错

    转:http://cgs1999.iteye.com/blog/1994072 1.引言网络地址转换(Network Address Translation,简称NAT)是一种在IP分组通过路由器或防 ...

  8. Golang flag包使用详解(一)

    概述 flag包提供了一系列解析命令行参数的功能接口 命令行语法 命令行语法主要有以下几种形式 -flag //只支持bool类型 -flag=x -flag x //只支持非bool类型 以上语法对 ...

  9. css 导航样式

    html  结构 <div class="nav-menu float-r"> <ul class="menu-item"> <l ...

  10. 杂项-WebService:WebService

    ylbtech-杂项-WebService:WebService Web service是一个平台独立的,低耦合的,自包含的.基于可编程的web的应用程序,可使用开放的XML(标准通用标记语言下的一个 ...