题目链接


三模数\(NTT\):

就是多模数\(NTT\)最后\(CRT\)一下...下面两篇讲的都挺明白的。

https://blog.csdn.net/kscla/article/details/79547242

https://blog.csdn.net/zhouyuheng2003/article/details/85561887

模数不是\(NTT\)模数,考虑用多个\(NTT\)模数分别卷积,最后\(CRT\)合并(由中国剩余定理,同余方程组在模\(M=\prod m_i\)的情况下解是唯一的)。

卷积后\(a_i\)的最大值是\((10^9)^2\times10^5=10^{23}\),所以需要选几个乘积\(>10^{23}\)的质数,比如\(m_1=998244353=2^{23}*119+1,\quad m_2=1004535809=2^{21}*479+1,\quad m_3=469762049=2^{26}*7+1\)。它们的原根都是\(3\)。

最后我们能求出三个同余方程:$$x\equiv c_1\ (\mathbb{mod}\ m_1)\x\equiv c_2\ (\mathbb{mod}\ m_2)\x\equiv c_3\ (\mathbb{mod}\ m_3)$$

我们可以用\(CRT\)合并两个,就有\(x\equiv c_4\ (\mathbb{mod}\ m_1m_2)\)。

设\(x=am_1m_2+c_4\equiv c_3\ (\mathbb{mod}\ m_3)\),就可以算出\(a\equiv\frac{c_3-c_4}{m_1m_2}\ (\mathbb{mod}\ m_3)\),有了\(a\)代回去就算出\(x\)啦(虽然是模\(m_3\)意义下求的\(a\),但是设\(a=km_3+b\),代回到\(x\)里模\(m_1m_2m_3\)就把\(km_3\)那项消掉啦,只剩下\(b\))(当然前两个式子也可以这么合并)。

这样一共需要\(9\)次\(DFT\),常数巨大。


拆系数\(FFT\)(\(MTT\))

\(FFT\)的问题在于精度,我们可以压缩值域。

令\(m=\lceil\sqrt p\rceil,\ A_i=a\times m+b,\ B_i=c\times m+d\),那么\(h_{i+j}=\sum(a_im+b_i)(c_jm+d_j)=\sum a_ic_jm^2+(a_id_j+b_ic_j)m+b_id_j\),分别求出四项(中间两项放一块算),就只需要\(7\)次\(DFT\)。

为了方便可以令\(m=2^{15}=32768\)。

这样\(FFT\)后的最大结果是\(10^5\times2^{30}=10^{14}\),注意取模。

注意std::sin的精度比sin高!因为值域依旧很大所以必须要注意这个,还要开long double

还有个挺重要的优化是预处理单位根,预处理\(lim\)次单位根的\(0\sim lim-1\)次方,用\(\omega_i^k\)时就是\(\omega_{lim}^{k\times\frac{lim}{i}}\)(怎么都预处理的\(2lim\)次单位根啊...)。

注意预处理的时候不要像\(FFT\)里那样每次w=w*Wn,而是对每个单位根直接用欧拉公式算,这样精度误差会小非常多,就可以把long double替换成double了...(然后就可以快很多很多)

(嗯...误差问题都出在求单位根上了...)

现在\(7\)次\(DFT\)已经挺快了...还可以优化,可以看毛啸的论文,或者这里以及这里。我咕了。


三模数\(NTT\):

//3786ms	8.95MB
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define G 3
#define MOD(x,mod) x>=mod&&(x-=mod)
#define ADD(x,v,mod) (x+=v)>=mod&&(x-=mod)
#define gc() getchar()
#define MAXIN 500000
//#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=(1<<18)+3,Mod[]={998244353,469762049,1004535809}; int tA[N],tB[N],A[N],B[N],Ans[3][N],rev[N];
char IN[MAXIN],*SS=IN,*TT=IN; inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-48,c=gc());
return now;
}
inline int FP(int x,int k,const int mod)
{
int t=1;
for(; k; k>>=1,x=1ll*x*x%mod)
if(k&1) t=1ll*t*x%mod;
return t;
}
inline LL Mult(LL a,LL b,LL p)
{
LL t=a*b-(LL)((long double)a/p*b+1e-8)*p;
return t<0?t+p:t;
}
void NTT(int *a,int lim,int opt,const int mod)
{
const int invG=FP(G,mod-2,mod);
for(int i=1; i<lim; ++i) if(i<rev[i]) std::swap(a[i],a[rev[i]]);//&&
for(int i=2; i<=lim; i<<=1)
{
int mid=i>>1,Wn=FP(~opt?G:invG,(mod-1)/i,mod);
for(int j=0; j<lim; j+=i)
for(int t,w=1,k=j; k<j+mid; ++k,w=1ll*w*Wn%mod)
a[k+mid]=a[k]+mod-(t=1ll*a[k+mid]*w%mod), MOD(a[k+mid],mod),
ADD(a[k],t,mod);
}
if(opt==-1) for(int i=0,inv=FP(lim,mod-2,mod); i<lim; ++i) a[i]=1ll*a[i]*inv%mod;
}
void Solve(const int lim,int *ans,const int mod)
{
memcpy(A,tA,lim<<2), memcpy(B,tB,lim<<2);//不能只赋值到n!(清空后面的)
NTT(A,lim,1,mod), NTT(B,lim,1,mod);
for(int i=0; i<lim; ++i) A[i]=1ll*A[i]*B[i]%mod;
NTT(A,lim,-1,mod);
for(int i=0; i<lim; ++i) ans[i]=A[i];
} int main()
{
const int n=read(),m=read(),P=read();
for(int i=0; i<=n; ++i) tA[i]=read(),tA[i]>=P&&(tA[i]%=P);
for(int i=0; i<=m; ++i) tB[i]=read(),tB[i]>=P&&(tB[i]%=P);
int lim=1,l=-1; while(lim<=n+m) lim<<=1,++l;
for(int i=1; i<lim; ++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<l);
for(int i=0; i<3; ++i) Solve(lim,Ans[i],Mod[i]);
#define m1 998244353ll
#define m2 469762049ll
#define m3 1004535809ll
const LL M12=m1*m2,inv2=FP(m2,m1-2,m1),inv1=FP(m1,m2-2,m2),mul2=m2*inv2%M12,mul1=m1*inv1%M12,
inv=FP(M12%m3,m3-2,m3),m12=M12%P;
for(int i=0,t=n+m; i<=t; ++i)
{
LL c1=Ans[0][i],c2=Ans[1][i],c3=Ans[2][i],c4=Mult(c1,mul2,M12)+Mult(c2,mul1,M12);
MOD(c4,M12);
LL a=((c3-c4)%m3+m3)/*%m3*/*inv%m3;
printf("%d ",(int)((a*m12+c4)%P));
} return 0;
}

\(MTT\):

//1094ms	26.14MB
#include <cmath>
#include <cstdio>
#include <cctype>
#include <algorithm>
#define gc() getchar()
#define MAXIN 300000
//#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
typedef long double ld;
//#define double long double
const int N=(1<<18)+3;
const double PI=acos(-1); int rev[N];
char IN[MAXIN],*SS=IN,*TT=IN;
struct Complex
{
double x,y;
Complex(double x=0,double y=0):x(x),y(y) {}
inline Complex operator +(const Complex &a)const {return Complex(x+a.x, y+a.y);}
inline Complex operator -(const Complex &a)const {return Complex(x-a.x, y-a.y);}
inline Complex operator *(const Complex &a)const {return Complex(x*a.x-y*a.y, y*a.x+x*a.y);}
}A[N],B[N],C[N],D[N],W[N]; inline int read()
{
int now=0; register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now;
}
void FFT(Complex *a,const int lim,const int opt)
{
static Complex w[N];
for(int i=1; i<lim; ++i) if(i<rev[i]) std::swap(a[i],a[rev[i]]);
for(int i=2; i<=lim; i<<=1)
{
int mid=i>>1;
if(~opt) for(int k=0,t=lim/i; k<mid; ++k) w[k]=W[k*t];
else for(int k=0,t=lim/i; k<mid; ++k) w[k]=Complex(W[k*t].x,-W[k*t].y);
for(int j=0; j<lim; j+=i)
{
Complex t;
for(int k=j; k<j+mid; ++k)
a[k+mid]=a[k]-(t=w[k-j]*a[k+mid]), a[k]=a[k]+t;
}
}
if(opt==-1) for(int i=0; i<lim; ++i) a[i].x/=lim;
} int main()
{
const int n=read(),m=read(),P=read();
for(int i=0,t; i<=n; ++i) t=read(),A[i].x=t>>15,B[i].x=t&32767;
for(int i=0,t; i<=m; ++i) t=read(),C[i].x=t>>15,D[i].x=t&32767; int lim=1,l=-1; while(lim<=n+m) lim<<=1,++l;
for(int i=0; i<lim; ++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<l);
for(int i=0,t=lim>>1; i<lim; ++i) W[i]=Complex(std::cos(i*PI/t),std::sin(i*PI/t));
// const Complex Wn(std::cos(2.0*PI/lim),std::sin(2.0*PI/lim)); Complex w(1,0);
// for(int i=0; i<lim; ++i) W[i]=w, w=w*Wn;//精度巨差 FFT(A,lim,1), FFT(B,lim,1), FFT(C,lim,1), FFT(D,lim,1);
for(int i=0; i<lim; ++i)
{
const Complex a=A[i],b=B[i],c=C[i],d=D[i];
A[i]=a*c, B[i]=a*d+b*c, D[i]=b*d;
}
FFT(A,lim,-1), FFT(B,lim,-1), FFT(D,lim,-1);
for(int i=0,t=n+m; i<=t; ++i)
{
LL res=((LL(A[i].x+0.5))%P<<30)+((LL(B[i].x+0.5))%P<<15)+(LL(D[i].x+0.5));
printf("%d ",(int)(res%P));
} return 0;
}

洛谷.4245.[模板]任意模数NTT(MTT/三模数NTT)的更多相关文章

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

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

  2. 【模板篇】NTT和三模数NTT

    之前写过FFT的笔记. 我们知道FFT是在复数域上进行的变换. 而且经过数学家的证明, DFT是复数域上唯一满足循环卷积性质的变换. 而我们在OI中, 经常遇到对xxxx取模的题目, 这就启发我们可不 ...

  3. 洛谷P3373 [模板]线段树 2(区间增减.乘 区间求和)

    To 洛谷.3373 [模板]线段树2 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.将某区间每一个数乘上x 3.求出某区间每一个数的和 输入输出格式 输入格 ...

  4. 洛谷 4245 【模板】任意模数NTT——三模数NTT / 拆系数FFT

    题目:https://www.luogu.org/problemnew/show/P4245 三模数NTT: 大概是用3个模数分别做一遍,用中国剩余定理合并. 前两个合并起来变成一个 long lon ...

  5. 洛谷.3803.[模板]多项式乘法(NTT)

    题目链接:洛谷.LOJ. 为什么和那些差那么多啊.. 在这里记一下原根 Definition 阶 若\(a,p\)互质,且\(p>1\),我们称使\(a^n\equiv 1\ (mod\ p)\ ...

  6. 洛谷.4721.[模板]分治FFT(NTT)

    题目链接 换一下形式:\[f_i=\sum_{j=0}^{i-1}f_jg_{i-j}\] 然后就是分治FFT模板了\[f_{i,i\in[mid+1,r]}=\sum_{j=l}^{mid}f_jg ...

  7. 洛谷.4512.[模板]多项式除法(NTT)

    题目链接 多项式除法 & 取模 很神奇,记录一下. 只是主要部分,更详细的和其它内容看这吧. 给定一个\(n\)次多项式\(A(x)\)和\(m\)次多项式\(D(x)\),求\(deg(Q) ...

  8. 洛谷P3375 [模板]KMP字符串匹配

    To 洛谷.3375 KMP字符串匹配 题目描述 如题,给出两个字符串s1和s2,其中s2为s1的子串,求出s2在s1中所有出现的位置. 为了减少骗分的情况,接下来还要输出子串的前缀数组next.如果 ...

  9. LCT总结——概念篇+洛谷P3690[模板]Link Cut Tree(动态树)(LCT,Splay)

    为了优化体验(其实是强迫症),蒟蒻把总结拆成了两篇,方便不同学习阶段的Dalao们切换. LCT总结--应用篇戳这里 概念.性质简述 首先介绍一下链剖分的概念(感谢laofu的讲课) 链剖分,是指一类 ...

随机推荐

  1. Linux之man命令详解及中文汉化

    使用方法 Linux man中的man就是manual的缩写,用来查看系统中自带的各种参考手册 使用方法: man command 示例: [root@VM_0_13_centos ~]# man l ...

  2. 数据结构C++实现代码-顺序表

    参考:https://blog.csdn.net/ebowtang/article/details/43094041 //seqList.h// //包含顺序表中的声明// #include<i ...

  3. IDEA创建第一个项目详细过程

  4. BZoj 2301 Problem b(容斥定理+莫比乌斯反演)

    2301: [HAOI2011]Problem b Time Limit: 50 Sec  Memory Limit: 256 MB Submit: 7732  Solved: 3750 [Submi ...

  5. Django主线

    Django怎么学: 参考地址:https://www.zhihu.com/question/26235428 需要了解的知识点: Django Url请求流程 首要操作 Django的安装 pip3 ...

  6. mysql主从复制(半同步方式)

    mysql主从复制(半同步方式) 博客分类: MySQL mysqlreplication复制  一.半同步复制原理介绍 1. 优点 当事务返回客户端成功后,则日志一定在至少两台主机上存在. MySQ ...

  7. train_test_split数据切分

    train_test_split 数据切分 格式: X_train,X_test, y_train, y_test =cross_validation.train_test_split(train_d ...

  8. Codeforces 765F Souvenirs 线段树 + 主席树 (看题解)

    Souvenirs 我们将询问离线, 我们从左往右加元素, 如果当前的位置为 i ,用一棵线段树保存区间[x, i]的答案, 每次更新完, 遍历R位于 i 的询问更新答案. 我们先考虑最暴力的做法, ...

  9. appium---第三个脚本,进入评论页,发表评论

    #进入编辑页,点击编辑框,固定光标 #com.xxx.xxx:id/edit__content drive.find_element_by_id('/edit_content').click(); t ...

  10. BZOJ1433 [ZJOI2009]假期的宿舍 二分图匹配 匈牙利算法

    原文链接http://www.cnblogs.com/zhouzhendong/p/8372785.html 题目传送门 - BZOJ1433 题解 我们理一理题目. 在校的学生,有自己的床,还可以睡 ...