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

用三模数NTT做,需要注意时间和细节;

注意各种地方要取模!传入 upt() 里面的数一定要不超过2倍 mod!

乘法会爆 long long 时用快速乘!

两次合并的模数,第一次是 (ll) p1*p2,第二次直接对题目的模数取模即可!

注意局部开 (ll)!

合并时用到的逆元每次都一样,所以要先处理好而不是现场快速幂算!!

然而为什么时间还是 Narh 的两倍!

一晚上的心血...

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
int const xn=(<<);
int n,m,lim,rev[xn],a[][xn],b[][xn],p[]={,,,};
ll mod;
int rd()
{
int ret=,f=; char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=; ch=getchar();}
while(ch>=''&&ch<='')ret=ret*+ch-'',ch=getchar();
return f?ret:-ret;
}
ll upt(ll x,int md){while(x>=md)x-=md; while(x<)x+=md; return x;}
ll mul(ll a,ll b,int md)
{
ll ret=; a=a%md; b=b%md;
if(a<)a+=md; if(b<)b+=md;//
for(;b;b>>=1ll,a=(a+a)%md)if(b&)ret=(ret+a)%md;
return ret;
}
ll pw(ll a,int b,int md)
{
ll ret=; a=a%md;
for(;b;b>>=,a=mul(a,a,md))if(b&)ret=mul(ret,a,md);//mul!!
return ret;
}
void ntt(int *a,int tp,int md)
{
for(int i=;i<lim;i++)
if(i<rev[i])swap(a[i],a[rev[i]]);
for(int mid=;mid<lim;mid<<=)
{
int wn=pw(,(md-)/(mid<<),md);
if(tp==-)wn=pw(wn,md-,md);
for(int j=,len=(mid<<);j<lim;j+=len)
{
int w=;
for(int k=;k<mid;k++,w=(ll)w*wn%md)
{
int x=a[j+k],y=(ll)w*a[j+mid+k]%md;
a[j+k]=upt(x+y,md); a[j+mid+k]=upt(x-y,md);
}
}
}
if(tp==)return; int inv=pw(lim,md-,md);
for(int i=;i<lim;i++)a[i]=(ll)a[i]*inv%md;
}
ll uni(ll r1,ll r2,ll m1,int m2,int tp,int inv)
{
ll k=mul(r2-r1,inv,m2);
if(!tp)return (r1+k*m1)%(m1*m2);
return upt((r1+mul(k,m1,mod))%mod,mod);//%mod!!
}
int main()
{
n=rd(); m=rd(); mod=rd();
for(int i=;i<=n;i++)a[][i]=a[][i]=a[][i]=rd();
for(int i=;i<=m;i++)b[][i]=b[][i]=b[][i]=rd();
lim=; int l=;
while(lim<=n+m+)lim<<=,l++;//+2!
for(int i=;i<lim;i++)
rev[i]=((rev[i>>]>>)|((i&)<<(l-)));
for(int i=;i<=;i++)
{
ntt(a[i],,p[i]); ntt(b[i],,p[i]);
for(int j=;j<lim;j++)a[i][j]=(ll)a[i][j]*b[i][j]%p[i];
ntt(a[i],-,p[i]);
}
int inv1=pw(p[],p[]-,p[]);
int inv2=pw((ll)p[]*p[],p[]-,p[]);//inv!!
for(int i=;i<=n+m;i++)
{
ll ans=uni(a[][i],a[][i],p[],p[],,inv1);
ans=uni(ans,a[][i],(ll)p[]*p[],p[],,inv2);//(ll)!!!
printf("%lld ",ans);
}
puts("");
return ;
}

关于拆系数FFT,这篇博客说得十分清晰:https://blog.csdn.net/lvzelong2014/article/details/80156989

而且代码也十分简洁优美,所以就模仿着写了;

注意:

1. 读入的初始数组要先取模;

2. 对 (x<<30) 开 (ll) 要写在括号里面而非外面;

3. IDFT中最后要 /lim,平常都之写 a[i].x/lim,但这里因为用到了 y,所以必须加上 a[i].y/lim!!

4. 卡精度,要开 long double

(注意数组范围,如果要用到 a[lim] 的话,再稍微把数组开大一点)

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
typedef long double db;//
int const xn=(<<)+;//a[lim]
db const Pi=acos(-1.0);
int n,m,lim,rev[xn],P,f[xn],g[xn],ans[xn];
struct com{db x,y;}a[xn],b[xn],Da[xn],Db[xn],Dc[xn],Dd[xn];
com operator + (com a,com b){return (com){a.x+b.x,a.y+b.y};}
com operator - (com a,com b){return (com){a.x-b.x,a.y-b.y};}
com operator * (com a,com b){return (com){a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x};}
com conj(com a){return (com){a.x,-a.y};}
int rd()
{
int ret=,f=; char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=; ch=getchar();}
while(ch>=''&&ch<='')ret=ret*+ch-'',ch=getchar();
return f?ret:-ret;
}
void init()
{
lim=; int l=;
while(lim<=n+m)lim<<=,l++;
for(int i=;i<lim;i++)rev[i]=((rev[i>>]>>)|((i&)<<(l-)));
}
int upt(int x){while(x>=P)x-=P; while(x<)x+=P; return x;}
void fft(com *a,int tp)
{
for(int i=;i<lim;i++)
if(i<rev[i])swap(a[i],a[rev[i]]);
for(int mid=;mid<lim;mid<<=)
{
com wn=(com){cos(Pi/mid),tp*sin(Pi/mid)};
for(int j=,len=(mid<<);j<lim;j+=len)
{
com w=(com){,};
for(int k=;k<mid;k++,w=w*wn)
{
com x=a[j+k],y=w*a[j+mid+k];
a[j+k]=x+y; a[j+mid+k]=x-y;
}
}
}
if(tp==)return;
for(int i=;i<lim;i++)a[i].x/=lim,a[i].y/=lim;//y!! for use y
}
void mul(int *A,int *B,int *C)
{
for(int i=;i<lim;i++)A[i]=upt(A[i]%P),B[i]=upt(B[i]%P);//
int M=(<<)-;
for(int i=;i<lim;i++)a[i]=(com){A[i]&M,A[i]>>};
for(int i=;i<lim;i++)b[i]=(com){B[i]&M,B[i]>>};
fft(a,); fft(b,);
a[lim]=a[]; b[lim]=b[];//
for(int i=,j=lim;i<lim;i++,j--)
{
com da,db,dc,dd;
da=(a[i]+conj(a[j]))*(com){0.5,};
db=(a[i]-conj(a[j]))*(com){,-0.5};
dc=(b[i]+conj(b[j]))*(com){0.5,};
dd=(b[i]-conj(b[j]))*(com){,-0.5};
Da[i]=da*dc; Db[i]=da*dd; Dc[i]=db*dc; Dd[i]=db*dd;
}
a[lim]=b[lim]=(com){,};
for(int i=;i<lim;i++)a[i]=Da[i]+Db[i]*(com){,};
for(int i=;i<lim;i++)b[i]=Dc[i]+Dd[i]*(com){,};
fft(a,-); fft(b,-);
for(int i=;i<=n+m;i++)
{
int da=(ll)(a[i].x+0.5)%P;
int db=(ll)(a[i].y+0.5)%P;
int dc=(ll)(b[i].x+0.5)%P;
int dd=(ll)(b[i].y+0.5)%P;
C[i]=(da+((ll)(db+dc)<<)+((ll)dd<<))%P;//(ll)
}
}
int main()
{
n=rd(); m=rd(); P=rd(); init();
for(int i=;i<=n;i++)f[i]=rd();
for(int i=;i<=m;i++)g[i]=rd();
mul(f,g,ans);
for(int i=;i<=n+m;i++)printf("%d ",upt(ans[i])); puts("");
return ;
}

洛谷 P4245 [模板]任意模数NTT —— 三模数NTT / 拆系数FFT(MTT)的更多相关文章

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

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

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

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

  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. [洛谷P4245]【模板】任意模数NTT

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

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

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

  8. 【洛谷P4245】 【模板】任意模数NTT

    三模数 NTT,感觉不是很难写 $?$ 代码借鉴的 https://www.cnblogs.com/Mychael/p/9297652.html code: #include <bits/std ...

  9. [题解] Luogu P4245 [模板]任意模数NTT

    三模NTT 不会... 都0202年了,还有人写三模NTT啊... 讲一个好写点的做法吧: 首先取一个阀值\(w\),然后把多项式的每个系数写成\(aw + c(c < w)\)的形式,换句话说 ...

随机推荐

  1. cas单点登录 deployerConfigContext.xml正确配置

    <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...

  2. Java中的BigInteger在ACM中的应用

    Java中的BigInteger在ACM中的应用 在ACM中的做题时,常常会遇见一些大数的问题.这是当我们用C或是C++时就会认为比較麻烦.就想有没有现有的现有的能够直接调用的BigInter,那样就 ...

  3. 醒醒吧少年,只用Cucumber不能帮助你BDD

    转载:http://insights.thoughtworkers.org/bdd/ 引言 在Ruby社区中,测试和BDD一直是被热议的话题,不管是单元测试.集成测试还是功能测试,你总能找到能帮助你的 ...

  4. 非常easy的JAVA反射教程

    原创文章,转载请注明. 反射能够动态载入类,实例化对象,调用方法.如今以下面样例解说. 一.载入类. Class clazz = Class.forName("java.lang.Strin ...

  5. JavaScript技巧手冊

    js小技巧 每一项都是js中的小技巧,但十分的有用! 1.document.write(""); 输出语句  2.JS中的凝视为//  3.传统的HTML文档顺序是:documen ...

  6. Odoo10尝鲜:MRP 10 新概念

    OEE [ overall equipment Effectiveness 整体设备效率 ] 整體設備效率是整合稼働率 (Availability).產能效率 (Performance).良率 (Qu ...

  7. android客户端向服务器端验证登陆方法的实现2

    一.在上一篇文章中,我只是提到了其中一种方法来实现登陆 大家可以参见: http://www.apkbus.com/android-45004-1-1.html      android获取web服务 ...

  8. shell(3):文本处理、基本语法和脚本编写

    一.awk.变量.运算符.if多分支 awk:shell编辑器的一种文本处理工具/命令,同grep.sed一样均可解释正则.具体运用下面awk文本处理有详细说明. 变量:分为系统变量和临时变量.变量一 ...

  9. swift学习_xcode6搭建

    首先是环境搭建 , 我的是苹果系统 , 我是个穷小子. 8k的电脑离我比較遥远. 自己动手的黑苹果 . 总价1k, 学习够用了即可.期间也学到了非常多东西 . 就是穷人仅仅能发时间去换钱了, 一直在考 ...

  10. 怎样高速编译mediatek\operator以下代码

    mediatek\operator以下有单独的apk.也有overlay的数据,单独的apk会配置anroid.mk,找到相应的路径直接build. 假设是overlay,则编译原来应用的路径,比如 ...