A * B Problem Plus

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 9413    Accepted Submission(s): 1468

Problem Description
Calculate A * B.
 
Input
Each line will contain two integers A and B. Process to end of file.

Note: the length of each integer will not exceed 50000.

 
Output
For each case, output A * B in one line.
 
Sample Input
1 2 1000 2
 
Sample Output
2 2000
 
Author
DOOM III
 
Recommend
DOOM III
 

就一个高精度乘法 FFT加速。

最近正好要捡起fft,就顺便整理了模板。

FFT的原理还是算法导论靠谱,没有那么艰深难懂,就涉及怎么进行FFT和FFT需要的原理和定理。

看看算法导论里FFT的部分,一定要读到迭代实现那部分!!

看了好久求和引理,才发觉他是为了保证$w_n^k$与$w_n^{k+2/h}$的对称性(即$w_n^{k+2/h}=-w_n^k$)的,这个引理是必要的。

对于多项式序列,我们可以用两个O(nlgn)(n>max(len1,len2)*2)的FFT将其系数表示转化为点值表示(DFT),然后用O(n) 相乘,接着用FFT把结果的点值表示变为系数表示(IDFT),总体算起来是3O(nlgn)+O(n),即O(nlgn)的时间复杂度。比O(n^2)好多了。

以下是学习的两个版本。

 #include<bits/stdc++.h>
#define clr(x) memset(x,0,sizeof(x))
#define clr_1(x) memset(x,-1,sizeof(x))
#define clrmax(x) memset(x,0x3f3f3f3f,sizeof(x))
#define LL long long
#define mod 1000000007
#define PI 3.1415926535
using namespace std;
char s1[],s2[];
int a[],b[];
//复数序列结构体
struct complexed
{
double r,i;
complexed(double _r=0.0,double _i=0.0)
{
r=_r;
i=_i;
}
complexed operator +(complexed b)
{
return complexed(r+b.r,i+b.i);
}
complexed operator -(complexed b)
{
return complexed(r-b.r,i-b.i);
}
complexed operator *(complexed b)
{
return complexed(r*b.r-i*b.i,i*b.r+r*b.i);
}
}num1,num2;
vector<complexed> multi1,multi2;
inline int max(int a,int b)
{
return a>b?a:b;
}
//并将长度变为2…^(k+1)
void changelen(int &len)
{
int mul=;
while(mul<len)
mul<<=;
mul<<=;
len=mul;
return ;
}
//将整数序列复制到复数序列中
void copyed(int *a,vector<complexed> &multi,int len)
{
multi.resize(len);
for(int i=;i<len;i++)
multi[i]=(complexed){a[i],};
return;
}
//DFT的话on=1,IDFT on=-1;
void fft(vector<complexed> &multi,int len,int on)
{
complexed wn,w,u,t;
//wn,w,u,t如算法导论中所示
vector<complexed> ans;
ans.resize(len);
//ans存每次操作计算后的y,最后再作为下次的multi。
for(int h=len/;h>=;h>>=)
{
wn=(complexed){cos(*on*PI/(len/h)),sin(*on*PI/(len/h))};
for(int i=;i<h;i++)
{
w=(complexed){,};
for(int j=;j<len/h/;j++)
{
//蝴蝶操作
u=multi[i+*h*j];
t=multi[i+*h*j+h]*w;
ans[i+h*j]=u+t;
ans[i+h*j+len/]=u-t;
w=w*wn;
}
}
//ans作为下次计算的multi
multi=ans;
}
//IDFT每个元素都得除以n
if(on==-)
for(int i=;i<len;i++)
multi[i].r/=len;
return ;
}
int main()
{
int len1,len2,len;
while(scanf("%s%s",s1,s2)!=EOF)
{
len1=strlen(s1);
len2=strlen(s2);
clr(a);
clr(b);
for(int i=;i<len1;i++)
{
a[len1-i-]=s1[i]-'';
}
for(int i=;i<len2;i++)
{
b[len2-i-]=s2[i]-'';
}
len=max(len1,len2);
//取长度较长者作为长度,并将长度变为2…^(k+1)
changelen(len);
//将两个整数序列复制到复数序列中
copyed(a,multi1,len);
copyed(b,multi2,len);
//对两个复数序列进行DFT,变为点值表示
fft(multi1,len,);
fft(multi2,len,);
//对应点点值相乘
for(int i=;i<len;i++)
multi1[i]=multi1[i]*multi2[i];
//将的出来的点值表示进行IDFT变为系数表示
fft(multi1,len,-);
//四舍五入减小损失精度
for(int i=;i<len;i++)
{
a[i]=(int)(multi1[i].r+0.5);
}
//进位
for(int i=;i<len;i++)
{
a[i+]=a[i+]+a[i]/;
a[i]%=;
}
len=len1+len2-;
//去掉前导0
while(a[len]<= && len>) len--;
for(int i=len;i>=;i--)
printf("%d",a[i]);
printf("\n");
}
return ;
}

无位逆序置换的步长实现

 #include<bits/stdc++.h>
#define clr(x) memset(x,0,sizeof(x))
#define clr_1(x) memset(x,-1,sizeof(x))
#define clrmax(x) memset(x,0x3f3f3f3f,sizeof(x))
#define LL long long
#define mod 1000000007
#define PI 3.1415926535
using namespace std;
char s1[],s2[];
int a[],b[];
struct complexed
{
double r,i;
complexed(double _r=0.0,double _i=0.0)
{
r=_r;
i=_i;
}
complexed operator +(complexed b)
{
return complexed(r+b.r,i+b.i);
}
complexed operator -(complexed b)
{
return complexed(r-b.r,i-b.i);
}
complexed operator *(complexed b)
{
return complexed(r*b.r-i*b.i,i*b.r+r*b.i);
}
}num1,num2;
complexed multi1[<<],multi2[<<];
inline int max(int a,int b)
{
return a>b?a:b;
}
void changelen(int &len)
{
int mul=;
while(mul<len)
mul<<=;
mul<<=;
len=mul;
return ;
}
//将整数序列复制到复数序列中
void copyed(int *a,complexed *multi,int len)
{
for(int i=;i<len;i++)
multi[i]=(complexed){a[i],};
return;
}
//位逆序变换
void bitchange(complexed *multi,int len)
{
int i,j,k;
for(i = , j = len/;i < len-; i++)
{
if(i < j)swap(multi[i],multi[j]);
k = len/;
while( j >= k)
{
j -= k;
k /= ;
}
if(j < k) j += k;
}
return ;
}
//DFT的话on=1,IDFT on=-1;
void fft(complexed *multi,int len,int on)
{
bitchange(multi,len);//位逆序置换
complexed wn,w,u,t;//如算法导论所示
for(int h=;h<=len;h<<=)
{
wn=(complexed){cos(*on*PI/h),sin(*on*PI/h)};
for(int i=;i<len;i+=h)
{
//蝴蝶操作
w=(complexed){,};
for(int j=i;j<i+h/;j++)
{
u=multi[j];
t=multi[j+h/]*w;
multi[j]=u+t;
multi[j+h/]=u-t;
w=w*wn;
}
}
}
//IDFT每个元素都得除以n
if(on==-)
for(int i=;i<len;i++)
multi[i].r/=len;
return ;
}
void mul(int *a,int *b,int &len1,int &len2)
{
int len=max(len1,len2);
//取长度较长者作为长度,并将长度变为2…^(k+1)
changelen(len);
//将两个整数序列复制到复数序列中
copyed(a,multi1,len);
copyed(b,multi2,len);
//对两个复数序列进行DFT,变为点值表示
fft(multi1,len,);
fft(multi2,len,);
//对应点点值相乘
for(int i=;i<len;i++)
multi1[i]=multi1[i]*multi2[i];
//将的出来的点值表示进行IDFT变为系数表示
fft(multi1,len,-);
//四舍五入减小损失精度
for(int i=;i<len;i++)
{
a[i]=(int)(multi1[i].r+0.5);
}
while(len-> && a[len-]==)
len--;
len1=len;
return ;
}
int main()
{
int len1,len2,len;
while(scanf("%s%s",s1,s2)!=EOF)
{
len1=strlen(s1);
len2=strlen(s2);
clr(a);
clr(b);
for(int i=;i<len1;i++)
{
a[len1-i]=s1[i]-'';
}
for(int i=;i<len2;i++)
{
b[len2-i]=s2[i]-'';
}
mul(a+,b+,len1,len2);
//进位
len=len1;
for(int i=;i<len;i++)
{
a[i+]=a[i+]+a[i]/;
a[i]%=;
}
while(a[len]>)
{
a[len+]=a[len+]+a[len]/;
a[len]%=;
len++;
}
for(int i=len;i>=;i--)
printf("%d",a[i]);
printf("\n");
}
return ;
}

位逆序置换的迭代实现

后来看了ntt,小改了下原迭代实现的模板,实现了迭代实现的NTT模板:

 #include<bits/stdc++.h>
#define clr(x) memset(x,0,sizeof(x))
#define clr_1(x) memset(x,-1,sizeof(x))
#define clrmax(x) memset(x,0x3f3f3f3f,sizeof(x))
#define LL long long
#define mod 1004535809
#define PI 3.1415926535
#define P 1004535809
#define G 3
using namespace std;
char s1[],s2[];
LL a[],b[],c[];
LL quick_pow(LL mul,LL n)
{
LL res=;
mul=(mul%mod+mod)%mod;
while(n)
{
if(n%)
res=res*mul%mod;
mul=mul*mul%mod;
n/=;
}
return res;
}
inline int max(int a,int b)
{
return a>b?a:b;
}
void bitchange(LL *a,int len)
{
int i,j,k;
for(i = , j = len>>;i < len-; i++)
{
if(i < j)swap(a[i],a[j]);
k = len>>;
while( j >= k)
{
j -= k;
k >>= ;
}
if(j < k) j += k;
}
return ;
}
void changelen(int &len)
{
int mul=;
while(mul<len)
mul<<=;
mul<<=;
len=mul;
return ;
}
//DFT的话on=1,IDFT on=-1;
void ntt(LL *a,int len,LL on)
{
bitchange(a,len);//位逆序置换
LL wn,w,u,t;//如算法导论所示
for(int h=;h<=len;h<<=)
{
wn=quick_pow(G,(P-)/h)%mod;
for(int i=;i<len;i+=h)
{
//蝴蝶操作
w=;
for(int j=i;j<i+h/;j++)
{
u=a[j]%mod;
t=a[j+h/]*w%mod;
a[j]=(u+t)%mod;
a[j+h/]=(u-t+mod)%mod;
w=w*wn%mod;
}
}
}
//IDFT调换次序实现wn^-1的情况,并且乘以len的逆元
if(on==-)
{
//k^0显然不调换次序,但是k^1与k^-1,k^2与k^-2.... k^len/2与k^-len/2 要调换次序
for(int i=;i<len/;i++)
swap(a[i],a[len-i]);
LL re=quick_pow(len,P-);
for(int i=;i<len;i++)
a[i]=a[i]*re%mod;
}
return ;
}
void mul(LL *a,LL *b,int &len1,int &len2)
{
int len=max(len1,len2);
//取长度较长者作为长度,并将长度变为2…^(k+1)
changelen(len);
//对两个整数序列进行DFT,变为点值表示
ntt(a,len,);
ntt(b,len,);
//对应点点值相乘
for(int i=;i<len;i++)
a[i]=b[i]*a[i]%mod;
//将的出来的点值表示进行IDFT变为系数表示
ntt(a,len,-);
while(len-> && a[len-]==)
len--;
len1=len;
return ;
}
int main()
{
int len1,len2,len;
while(scanf("%s%s",s1,s2)!=EOF)
{
len1=strlen(s1);
len2=strlen(s2);
clr(a);
clr(b);
for(int i=;i<len1;i++)
{
a[len1-i]=s1[i]-'';
}
for(int i=;i<len2;i++)
{
b[len2-i]=s2[i]-'';
}
mul(a+,b+,len1,len2);
//进位
len=len1;
for(int i=;i<len;i++)
{
a[i+]=a[i+]+a[i]/;
a[i]%=;
}
while(a[len]>)
{
a[len+]=a[len+]+a[len]/;
a[len]%=;
len++;
}
for(int i=len;i>=;i--)
printf("%lld",a[i]);
printf("\n");
}
return ;
}

NTT的迭代实现

NTT需要爆搜下找到该质数的原根(这部分一般不写到代码里,一般是自己找出来以后再直接作为常量放在程序里,建议分解完P-1的质因数后去搜索快点,一般原根都不太大比较好搜)。在比赛中一般给出的质数P,P-1后一般是C*2^k的形式,才能支持2^k的分治。

学习资料推荐:http://blog.sina.com.cn/s/blog_7c4c33190102wht6.html 这个看下原理一类的,包括FFT的。其中笔者把(P-1)*2^m写错写成了P*2^m了。

代码以及等价性参考ACdreamer的代码:http://blog.csdn.net/acdreamers/article/details/39026505

hdu 1402(FFT乘法 || NTT乘法)的更多相关文章

  1. HDU 1402 FFT 大数乘法

    $A * B$ FFT模板题,找到了一个看起来很清爽的模板 /** @Date : 2017-09-19 22:12:08 * @FileName: HDU 1402 FFT 大整数乘法.cpp * ...

  2. HDU 1402 fft 模板题

    题目就是求一个大数的乘法 这里数字的位数有50000的长度,按平时的乘法方式计算,每一位相乘是要n^2的复杂度的,这肯定不行 我们可以将每一位分解后作为系数,如153 = 1*x^2 + 5*x^1 ...

  3. hdu 1402 FFT(模板)

    A * B Problem Plus Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Other ...

  4. 多项式乘法,FFT与NTT

    多项式: 多项式?不会 多项式加法: 同类项系数相加: 多项式乘法: A*B=C $A=a_0x^0+a_1x^1+a_2x^2+...+a_ix^i+...+a_{n-1}x^{n-1}$ $B=b ...

  5. hdu 1402 A * B Problem Plus FFT

    /* hdu 1402 A * B Problem Plus FFT 这是我的第二道FFT的题 第一题是完全照着别人的代码敲出来的,也不明白是什么意思 这个代码是在前一题的基础上改的 做完这个题,我才 ...

  6. 洛谷P3803 【模板】多项式乘法 [NTT]

    题目传送门 多项式乘法 题目描述 给定一个n次多项式F(x),和一个m次多项式G(x). 请求出F(x)和G(x)的卷积. 输入输出格式 输入格式: 第一行2个正整数n,m. 接下来一行n+1个数字, ...

  7. hdu 5187 高速幂高速乘法

    http://acm.hdu.edu.cn/showproblem.php?pid=5187 Problem Description As one of the most powerful brush ...

  8. HDU 3074.Multiply game-区间乘法-线段树(单点更新、区间查询),上推标记取模

    Multiply game Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Tot ...

  9. A * B Problem Plus HDU - 1402 (FFT)

    A * B Problem Plus HDU - 1402 (FFT) Calculate A * B.  InputEach line will contain two integers A and ...

随机推荐

  1. oracle 的number数据类型

    NUMBER类型细讲:Oracle number datatype 语法:NUMBER[(precision [, scale])]简称:precision --> p      scale   ...

  2. <script>标签的属性

    1.async 表示立即下载资源,但不妨碍下载其他资源或等待加载其他脚本,脚本相对于页面的其它部分异步执行,只对外部脚本文件有效 2.defer 表示脚本可以延迟到文档完全被解析和显示之后再执行,只对 ...

  3. [Leetcode Week17]Copy List with Random Pointer

    Copy List with Random Pointer 题解 原创文章,拒绝转载 题目来源:https://leetcode.com/problems/copy-list-with-random- ...

  4. python基础===基于requests模块上的协程【trip】

    今天看博客get了一个有趣的模块,叫做 trip     #(pip install  trip) 兼容2.7版本 基于两大依赖包:TRIP: Tornado & Requests In Pa ...

  5. Linux内核基础--事件通知链(notifier chain)【转】

    转自:http://blog.csdn.net/wuhzossibility/article/details/8079025 内核通知链 1.1. 概述 Linux内核中各个子系统相互依赖,当其中某个 ...

  6. ADO POST时出现“无法为更新定位行,一些值可能已在最后一次读取后已更改”问题的解决方法

    原因有这样几种: 1.在数据库设计时,为某些字段设置了默认值,在修改进行提交以后,数据库会自动修改对应字段的所有行的默认值,从而导致了数据库与数据集中数据的不一致,使ADOQuery无法对数据集进行定 ...

  7. caffe Python API 之InnerProduct

    net.fc3 = caffe.layers.InnerProduct(net.pool1, num_output=1024, weight_filler=dict(type='xavier'), b ...

  8. linux sftp and unix ftp

    sftp 1.功能作用 sftp 是一个交互式文件传输程式.它类似于 ftp, 但它进行加密传输,比FTP有更高的安全性. 2.位置 /usr/bin/sftp 3.格式用法 sftp [-1246C ...

  9. tornado 模版

    tornado 模版语法 取消转义 : 取消项目转义 :autoescape = None 取消模版转义:{% autoescape None %} 取消行转义   :{% raw bd %} 强制转 ...

  10. C#面向对象(OOP)入门—第一天—多态和继承(方法重载)

    面向对象是什么 面向对象是一种基于对象的编程方法,它取代了仅仅依靠方法和流程的编程方式.面向对象的编程语言中,对象(object)其实就是指特定类型.或某个类的实例.面向对象使得编程人员更容易组织和管 ...