洛谷 P4245 [模板]任意模数NTT —— 三模数NTT / 拆系数FFT(MTT)
题目: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)的更多相关文章
- 洛谷.4245.[模板]任意模数NTT(MTT/三模数NTT)
题目链接 三模数\(NTT\): 就是多模数\(NTT\)最后\(CRT\)一下...下面两篇讲的都挺明白的. https://blog.csdn.net/kscla/article/details/ ...
- 洛谷 4245 【模板】任意模数NTT——三模数NTT / 拆系数FFT
题目:https://www.luogu.org/problemnew/show/P4245 三模数NTT: 大概是用3个模数分别做一遍,用中国剩余定理合并. 前两个合并起来变成一个 long lon ...
- 洛谷P3373 [模板]线段树 2(区间增减.乘 区间求和)
To 洛谷.3373 [模板]线段树2 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.将某区间每一个数乘上x 3.求出某区间每一个数的和 输入输出格式 输入格 ...
- 【洛谷5月月赛】玩游戏(NTT,生成函数)
[洛谷5月月赛]玩游戏(NTT,生成函数) 题面 Luogu 题解 看一下要求的是什么东西 \((a_x+b_y)^i\)的期望.期望显然是所有答案和的平均数. 所以求出所有的答案就在乘一个逆元就好了 ...
- 拆系数FFT(任意模数FFT)
拆系数FFT 对于任意模数 \(mod\) 设\(m=\sqrt {mod}\) 把多项式\(A(x)\)和\(B(x)\)的系数都拆成\(a\times m+b\)的形式,时\(a, b\)都小于\ ...
- [洛谷P4245]【模板】任意模数NTT
题目大意:给你两个多项式$f(x)$和$g(x)$以及一个模数$p(p\leqslant10^9)$,求$f*g\pmod p$ 题解:任意模数$NTT$,最大的数为$p^2\times\max\{n ...
- 洛谷P4245 【模板】MTT(任意模数NTT)
题目背景 模板题,无背景 题目描述 给定 22 个多项式 F(x), G(x)F(x),G(x) ,请求出 F(x) * G(x)F(x)∗G(x) . 系数对 pp 取模,且不保证 pp 可以分解成 ...
- 【洛谷P4245】 【模板】任意模数NTT
三模数 NTT,感觉不是很难写 $?$ 代码借鉴的 https://www.cnblogs.com/Mychael/p/9297652.html code: #include <bits/std ...
- [题解] Luogu P4245 [模板]任意模数NTT
三模NTT 不会... 都0202年了,还有人写三模NTT啊... 讲一个好写点的做法吧: 首先取一个阀值\(w\),然后把多项式的每个系数写成\(aw + c(c < w)\)的形式,换句话说 ...
随机推荐
- Java中字符串转为16进制表示
Java中字符串转为16进制表示 String str = "鲸"; char[] chars = "0123456789ABCDEF".toCharArray ...
- Error building Player: Win32Exception: ApplicationName='E:/adt-20140702/sdk\tools\zipalign.exe', Com
1.原因 更新sdk后报错..由于版本号不同,zipalign.exe所处路径不同 2.解决的方法 在sdk路径下搜索zipalign.exe .然后拷贝到报错内容中制定的路径即可了.
- visual c++ 2013进行MySQL编程(ODBC) -- (一) 套装安装
最近写了有些技术类文章了,只因为最近研究多了些东西,有一些项目用到了,所以自己记录一下,怕自己忘记,如果有哪位同学有自己的见解,可以邮件或者回复,技术类的探讨,不管对否,都是欢迎的. 操作之前,必须安 ...
- mt-datetime-picker type="date" 时间格式 bug
1.mint-ui 中 mt-datetime-picker 组件,存在日期格式 bug <!-- 日期选择器 --> <template> <div class=&qu ...
- C++类的大小(转)
一个空类class A{};的大小为什么是1,因为如果不是1,当定义这个类的对象数组时候A objects[5]; objects[0]和objects[1]就在同一个地址处,就无法区分. 单继承 # ...
- 树状数组求最大值 (RMQ with Shifts)
代码: #include <iostream> #include <stdio.h> #include <string.h> #include <stdlib ...
- D3.js 制作中国地图 .net 公共基础类
D3.js 制作中国地图 from: http://d3.decembercafe.org/pages/map/index.html GeoJSON is a format for encoding ...
- 【每日Scrum】第二天(4.12) TD学生助手Sprint1站立会议
TD学生助手Sprint1站立会议(4.12) 任务看板 站立会议内容 组员 昨天 今天 困难 签到 刘铸辉 (组长) 做了几个Sqlite编辑事件导入数据库没成功,就编辑图片滑动显示功能 今天学习了 ...
- beifen---http://vdisk.weibo.com/s/uhCtnyUhD0Ooc
- vue入门-常用指令操作
指令:v-xx组成的特殊指令,如果一个标签中有指令会默认替换原有的书 v-model:实现数据和视图的双向绑定 v-text:在元素中插入值 v-html:在元素中插入标签或者插入文本 v-if:根据 ...