题目:https://www.luogu.org/problemnew/show/P4245

三模数NTT:

  大概是用3个模数分别做一遍,用中国剩余定理合并。

  前两个合并起来变成一个 long long 的模数,再要和第三个合并的话就爆 long long ,所以可以用一种让两个模数的乘积不出现的方法:https://blog.csdn.net/qq_35950004/article/details/79477797

  x*m1+a1 = -y*m2 + a2  <==>  x*m1+y*m2 = a2-a1  <==>  x*m1 = a2-a1 (mod m2)  <==> x=(a2-a1)*m1^{-1} (mod m2)

  然后根据该博客里的证明,在mod m2意义下算出来的 x 就是真的 x 。这样的话答案就是 x*m1+a1 ,可以在快速乘的过程中对题目中给的模数取模,就不会爆 long long 啦。

  注意输入的 a[ ] 和 b[ ] 不能 ntt( ,0, ) 之后再 ntt( ,1, ) 回来,因为值已经模了刚才那个模数了;所以要多开一些数组。

  注意输入进 mul 里的 a 和 b 应该是正的,不然没法 b>>=1 之类的。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=1e5+;
int m[]={,,};
int n0,n1,mod,len,r[N<<],a[][N<<],b[][N<<],c[][N<<];
ll M=(ll)m[]*m[],d[N<<];
int rdn()
{
int ret=;bool fx=;char ch=getchar();
while(ch>''||ch<''){if(ch=='-')fx=;ch=getchar();}
while(ch>=''&&ch<='') ret=ret*+ch-'',ch=getchar();
return fx?ret:-ret;
}
void upd(ll &x,ll md){x>=md?x-=md:;}
void upd(int &x,ll md){x>=md?x-=md:;}
ll mul(ll a,ll b,ll md)
{
a%=md; b%=md;//
ll ret=;while(b){if(b&1ll)ret+=a,upd(ret,md);a+=a;upd(a,md);b>>=1ll;}return ret;
}
ll pw(ll x,ll k,ll md)
{ll ret=;while(k){if(k&1ll)ret=mul(ret,x,md);x=mul(x,x,md);k>>=1ll;}return ret;}
void ntt(int *a,bool fx,int md)
{
for(int i=;i<len;i++)
if(i<r[i])swap(a[i],a[r[i]]);
for(int R=;R<=len;R<<=)
{
int m=R>>;
int Wn=pw(,(md-)/R,md);
fx?Wn=pw(Wn,md-,md):;
for(int i=;i<len;i+=R)
for(int j=,w=;j<m;j++,w=(ll)w*Wn%md)
{
int tmp=(ll)w*a[i+m+j]%md;
a[i+m+j]=a[i+j]+md-tmp; upd(a[i+m+j],md);
a[i+j]=a[i+j]+tmp; upd(a[i+j],md);
}
}
if(!fx)return; int inv=pw(len,md-,md);
for(int i=;i<len;i++) a[i]=(ll)a[i]*inv%md;
}
int main()
{
n0=rdn()+; n1=rdn()+; mod=rdn();
for(int i=;i<n0;i++)a[][i]=a[][i]=a[][i]=rdn();
for(int i=;i<n1;i++)b[][i]=b[][i]=b[][i]=rdn();
for(len=;len<=n0+n1;len<<=);
for(int i=;i<len;i++)r[i]=(r[i>>]>>)+((i&)?len>>:);
for(int i=;i<;i++)//don't ntt(a,1,m[i]) for it can't return(already mod)
{
ntt(a[i],,m[i]); ntt(b[i],,m[i]);
for(int j=;j<len;j++)c[i][j]=(ll)a[i][j]*b[i][j]%m[i];
ntt(c[i],,m[i]);
} ll inv=pw(m[],m[]-,m[]),t;
for(int i=,lm=n0+n1-;i<lm;i++)
{
t=mul((c[][i]-c[][i])%m[]+m[],inv,m[]);
d[i]=(mul(t,m[],M)+c[][i])%M;
}
inv=pw(M,m[]-,m[]);
for(int i=,lm=n0+n1-;i<lm;i++)
{
t=mul((c[][i]-d[i])%m[]+m[],inv,m[]);
d[i]=(mul(t,M,mod)+d[i])%mod;
printf("%lld ",d[i]);
}
puts(""); return ;
}

拆系数FFT:

  参考材料:https://blog.csdn.net/lvzelong2014/article/details/80156989

  因为数值最大可能 10^(9+9+5) ,double 的精度可能很不好。所以把 a[ i ] 拆成 k*m + b ,其中 m 大约是 sqrt(mod) ;

  这样的话2个多项式变成了4个多项式,卷积的时候只卷积 k 和 b ,不用带上 m ,数值最大就是 sqrt(mod) 级别的,精度就能行了。

  但 ( k1 + b1 )*( k2 + b2 ) = k1*k2 + b1*k2 + k1*b2 + b1*b2 ,需要4次卷积,做8次DFT(似乎可以弄成7次),太慢了。

  所以就像那个博客里说的那样:

  令 \( P(x) = A(x)+i*B(x) , Q(x) = A(x)-i*B(x) \) (写的时候就是把 a[ i ] 放在 p[ i ] 的实部、把 b[ i ] 放在 p[ i ] 的虚部)

  则 \( Q(w_{n}^{k}) = conj(P(w_{n}^{-k})) \)(这里已经代入了点值。即把 P DFT之后,可以根据它得到 Q 的点值)(证明见那个博客)

  然后由 P(x) 和 Q(x) 的定义,得

  \( A(w_{n}^{k}) = \frac{P(w_{n}^{k})+Q(w_{n}^{k})}{2} \)(点值)

  \( i*B(w_{n}^{k}) = \frac{P(w_{n}^{k})-Q(w_{n}^{k})}{2} \) 即 \( B(w_{n}^{k}) = i*\frac{Q(w_{n}^{k})-P(w_{n}^{k})}{2} \)

  把向量写成 ( 实部+ i * 虚部 ) 的样子,就能把 i 代进去,从而得到 A(x) 和 B(x) 的点值向量。这种方法利用 P(x) 和 Q(x) 的关联,只 DFT 了一下P(x),就求出了A(x)和B(x)两个多项式的点值!

  所以 DFT 两次,求出 k1、b1、k2、b2 的点值。相乘一番,得到 k1*k2 、b1*k2 、k1*b2 、b1*b2 的点值。

  考虑 iDFT 回去。还是设 \( P(w_{n}^{k}) = A(w_{n}^{k}) + i*B(w_{n}^{k})\)(点值),写的时候还是把向量写开,就能把 i 代进去,得到 \( P(w_{n}^{k}) \) 的向量。

  这时的 \( P(w_{n}^{k}) \) 是一组点值, iDFT 回去后直接是系数的 \( A(x) + i*B(x) \) !也就是 iDFT 后 p[ i ] 的实部是 A(x) 的系数 a[ i ] ,虚部是 b[ i ] 。

    这个的证明可以看那个博客,大概就是如果系数是( A 的系数 + B 的系数 )的话,点值也应该是 ( A 的点值 + B 的点值 );倒着推一下得到点值是 ( A 的点值 + B 的点值 ) 的话,iDFT 回去的系数也应该是 ( A 的系数 + B 的系数 ) 。

  这里的A(x)和B(x)就是 k1*k2 、b1*k2 、k1*b2 、b1*b2 中的两个。两次 iDFT 后就得到了这4个东西的系数表达!

  然后统计答案就行了。注意乘上 m2 或者 m 。

  这道题得开 long double 。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define db long double
#define ll long long
using namespace std;
const int N=1e5+,M=(<<)+; const db pi=acos(-);
int mod,a[N],b[N],len,r[M],ans[M];
struct cpl{db x,y;}pa[M],pb[M],Ta[M],Tb[M],Tc[M],Td[M],I;
cpl operator+ (cpl a,cpl b){return (cpl){a.x+b.x,a.y+b.y};}
cpl operator- (cpl a,cpl b){return (cpl){a.x-b.x,a.y-b.y};}
cpl operator* (cpl a,cpl b){return (cpl){a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x};}
cpl cnj(cpl a){return (cpl){a.x,-a.y};}
void upd(int &x){x>=mod?x-=mod:;}
int rdn()
{
int ret=;bool fx=;char ch=getchar();
while(ch>''||ch<''){if(ch=='-')fx=;ch=getchar();}
while(ch>=''&&ch<='') ret=ret*+ch-'',ch=getchar();
return fx?ret:-ret;
}
void fft(cpl *a,bool fx)
{
for(int i=;i<len;i++)
if(i<r[i])swap(a[i],a[r[i]]);
for(int R=;R<=len;R<<=)
{
int m=R>>;
cpl Wn=(cpl){ cos(pi/m),fx?-sin(pi/m):sin(pi/m) };
for(int i=;i<len;i+=R)
{
cpl w=I;
for(int j=;j<m;j++,w=w*Wn)
{
cpl x=a[i+j],y=w*a[i+m+j];
a[i+j]=x+y; a[i+m+j]=x-y;
}
}
}
if(!fx)return;
for(int i=;i<len;i++)a[i].x/=len,a[i].y/=len;//y!! for use y
}
void solve(int n,int *a,int m,int *b)
{
int bin=(<<)-; cpl ta,tb,tc,td; for(int i=;i<=n;i++)pa[i]=(cpl){a[i]>>,a[i]&bin};//pa=A+iB(a=km+b,A=k,B=b)
for(int i=;i<=m;i++)pb[i]=(cpl){b[i]>>,b[i]&bin};//<=m!!!
for(len=;len<=n+m;len<<=);
for(int i=;i<len;i++)r[i]=(r[i>>]>>)+((i&)?len>>:);
fft(pa,); fft(pb,);
pa[len]=pa[]; pb[len]=pb[];
for(int i=,j=len;i<len;i++,j--)//j=-i+len(if i=0 then j=0),qa[i]=cnj(pa[j])
{
//ta:point value of A
ta=(pa[i]+cnj(pa[j]))*(cpl){0.5,};//ta={(pa[i].x+pa[j].x)/2,(pa[i].y+pa[j].y)/2}
tb=(pa[i]-cnj(pa[j]))*(cpl){,-0.5};//tb=i(Q-P)/2=i*((Qx-Px)+i(Qy-Py))/2={(Py-Qy)/2,(Qx-Px)/2}
tc=(pb[i]+cnj(pb[j]))*(cpl){0.5,};
td=(pb[i]-cnj(pb[j]))*(cpl){,-0.5};
Ta[i]=ta*tc;Tb[i]=ta*td;Tc[i]=tb*tc;Td[i]=tb*td;
}
pa[len]=pb[len]=(cpl){,};
for(int i=;i<len;i++)pa[i]=Ta[i]+Tb[i]*(cpl){,};//pa=Ta+i*Tb={Tax-Tby,Tay+Tbx}
for(int i=;i<len;i++)pb[i]=Tc[i]+Td[i]*(cpl){,};
fft(pa,); fft(pb,);//pa.x=Ta,pa.y=Tb
for(int i=,j=n+m;i<=j;i++)
{
int Da=(ll)(pa[i].x+0.5)%mod;
int Db=(ll)(pa[i].y+0.5)%mod;
int Dc=(ll)(pb[i].x+0.5)%mod;
int Dd=(ll)(pb[i].y+0.5)%mod;
ans[i]=(((ll)Da<<) + ((ll)(Db+Dc)<<) + Dd)%mod+mod; upd(ans[i]);
}
}
int main()
{
int n,m; I.x=;
n=rdn();m=rdn();mod=rdn();
for(int i=;i<=n;i++)a[i]=rdn()%mod+mod,upd(a[i]);
for(int i=;i<=m;i++)b[i]=rdn()%mod+mod,upd(b[i]);
solve(n,a,m,b);
for(int i=,j=n+m;i<=j;i++)printf("%d ",ans[i]);puts("");
return ;
}

洛谷 4245 【模板】任意模数NTT——三模数NTT / 拆系数FFT的更多相关文章

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

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

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

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

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

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

  4. 【洛谷5月月赛】玩游戏(NTT,生成函数)

    [洛谷5月月赛]玩游戏(NTT,生成函数) 题面 Luogu 题解 看一下要求的是什么东西 \((a_x+b_y)^i\)的期望.期望显然是所有答案和的平均数. 所以求出所有的答案就在乘一个逆元就好了 ...

  5. 拆系数FFT(任意模数FFT)

    拆系数FFT 对于任意模数 \(mod\) 设\(m=\sqrt {mod}\) 把多项式\(A(x)\)和\(B(x)\)的系数都拆成\(a\times m+b\)的形式,时\(a, b\)都小于\ ...

  6. 洛谷4245:【模板】任意模数NTT——题解

    https://www.luogu.org/problemnew/show/P4245 给两个多项式,求其乘积,每个系数对p取模. 参考: 代码与部分理解参考https://www.luogu.org ...

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

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

  8. 洛谷 P5206 - [WC2019]数树(集合反演+NTT)

    洛谷题面传送门 神仙多项式+组合数学题,不过还是被我自己想出来了( 首先对于两棵树 \(E_1,E_2\) 而言,为它们填上 \(1\sim y\) 使其合法的方案数显然是 \(y\) 的 \(E_1 ...

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

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

随机推荐

  1. JavaScript笔记03——文档对象模型(Document Object Model,简称DOM):获取HTML元素、操作HTML元素

    Dom技术使得用户页面可以动态地变化,如可以动态地显示或隐藏一个元素,改变它们的属性,增加一个元素等,Dom技术使得页面的交互性大大地增强.[1] DOM实际上是以面向对象方式描述的文档模型.DOM定 ...

  2. 20145231 《Java程序设计》第一周学习总结

    20145231 <Java程序设计>第一周学习总结 教材学习内容总结 Java三大平台Java SE,Java EE,Java ME.其中,Java SE是我们学习的基础. Java S ...

  3. Kubernetes 待学习列表

    1.EFK or ELK https://blog.csdn.net/mawming/article/details/78344939, https://www.jianshu.com/p/fe3ac ...

  4. poj 1679 The Unique MST 【次小生成树+100的小数据量】

    题目地址:http://poj.org/problem?id=1679 2 3 3 1 2 1 2 3 2 3 1 3 4 4 1 2 2 2 3 2 3 4 2 4 1 2 Sample Outpu ...

  5. poj 1573 Robot Motion【模拟题 写个while循环一直到机器人跳出来】

                                                                                                         ...

  6. Java I/O 小结

    主要内容: 一.输入流基类:InputStream 和 OutputStream(字节流). Reader 和 Writer(字符流) 二.文件字节流:FileInputStream和FileOutp ...

  7. Centos6.5安装php5.6.7

    1. 下载 官网:http://php.net/downloads.php wget http://cn2.php.net/get/php-5.6.7.tar.gz/from/this/mirror ...

  8. Centos6.5下Hbase安装

    下载 http://mirror.bit.edu.cn/apache/hbase/hbase-0.94.26/hbase-0.94.26.tar.gz 2.  解压 tar -zxvf hbase-0 ...

  9. SpringBoot-新建项目

    在开发SpringBoot之前,先下载STS开发工具,当然也可以用myeclipse等工具. STS官方下载地址:https://spring.io/tools/sts 下载安装完成后:File--& ...

  10. java项目 里的DAO,model,service, IMPL含义

    在一般工程中 基本上都会出现上述的字眼首先 DAO 提供了应用程序与数据库之间的操作规范 和操作 用于通常数据库的增删查改 一般如果使用框架 都是由框架自动生成,提高访问效率和便于快速开发.hiber ...