题目: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. java中自带时间类使用方法实例 Date,Timestamp,DateFormat

    我们将以Java自带的时间日期类和当中的处理函数进行分析. 一.与时间日期有关的类. java.util.Date. 实现类,其对象具有时间.日期组件. java.util.Calendar. 抽象类 ...

  2. Windows 编程1

    杀死一个进程 使用命令 system( taskkill /f /im     QQ.exe); 即可. 打开一个进程 使用命令 system("应用程序的位置");   头文件: ...

  3. mysql 查询表索引的命令详解

    http://hi.baidu.com/wylinux/item/cbc458c2984381300831c651查看索引命令mysql> show index from tblname;mys ...

  4. 电源滤波电容在PCB中正确的布线方法!

    电源滤波电容在PCB中正确的布线方法! 错误的电源滤波电容布线方法. 1.很多人朋友在设计的时候喜欢加宽这个电源的走,这个是一个很好的方法,但是他们如果一不小心就会忽略电容的布线. 下面的电容布线看起 ...

  5. 轻松搞定RabbitMQ(六)——主题

    转自 http://blog.csdn.net/xiaoxian8023/article/details/48806871 翻译地址:http://www.rabbitmq.com/tutorials ...

  6. bvlc_reference_caffenet.caffemodel

    #uncoding:utf-8 # set up Python environment: numpy for numerical routines, and matplotlib for plotti ...

  7. AngularJs 在控制器中过滤

    <html> <head> <title>Simple app</title> <script src="https://ajax.go ...

  8. Tomcat appears to still be running with PID 19564. Start aborted

    产生原因:tomcat 异常关闭, 或强行终止导致(如断电等....) 如你所见 .  tomcat 在linux  关, 关不了. 开开不了. 疯狂百度一个小时以后,大致产生问题的原因是,LINUX ...

  9. Spring Cloud Config的配置中心获取不到最新配置信息的问题

    Spring Cloud Config的配置中心获取不到最新配置信息的问题 http://blog.didispace.com/spring-cloud-tips-config-tmp-clear/

  10. codevs1032

    题目地址:http://codevs.cn/problem/1032/ 分析: 题目数据有错.这题过不了才正常. 我调了非常久可是就是有两个点过不去.于是我把数据下了下来,找到WA的第五个点和第七个点 ...