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. 如何设计一个优雅健壮的Android WebView?(下)

    转:如何设计一个优雅健壮的Android WebView?(下) 前言 在上文<如何设计一个优雅健壮的Android WebView?(上)>中,笔者分析了国内WebView的现状,以及在 ...

  2. Katu Puzzle(POJ3678+2-SAT问题+tarjan缩点)

    题目链接:http://poj.org/problem?id=3678 题目: 题意:给你a,b,c,op,op为逻辑运算符或.与.异或,使得a op b = c,让你判断这些运算符是否存在矛盾,不存 ...

  3. 贿赂囚犯 Bribe the prisoners ( 动态规划+剪枝)

    一个监狱里有P个并排着的牢房,从左往右一次编号为1,2,-,P.最初所有牢房里面都住着一个囚犯.现在要释放一些囚犯.如果释放某个牢房里的囚犯,必须要贿赂两边所有的囚犯一个金币,直到监狱的两端或者空牢房 ...

  4. 【ALB学习笔记】基于多线程方式的串行通信接口数据接收案例

    基于多线程方式的串行通信接口数据接收案例 广东职业技术技术学院  欧浩源 1.案例背景 在本博客的<[CC2530入门教程-06]CC2530的ADC工作原理与应用>中实现了电压数据采集的 ...

  5. 【HNOI】 c tree-dp

    [题目描述]给定一个n个节点的树,每个节点有两个属性值a[i],b[i],我们可以在树中选取一个连通块G,这个连通块的值为(Σa[x])(Σb[x]) x∈G,求所有连通块的值的和,输出答案对1000 ...

  6. 解决嵌套GridView显示不全的问题

    package com.adan.selectcitydome.view; import android.content.Context; import android.util.AttributeS ...

  7. arduino 用电位器调节LED闪烁频率

    int dianwei; int led = 13; void setup() {  // put your setup code here, to run once:  Serial.begin(9 ...

  8. mac系统中实现vitualBox中访问内网端口

    第一步,增加外网网段 打开vitualbox后,按管理菜单,点击->主机网络管理器,如图1所示.点击创建,创建下个网络主机. 图1 然后,关掉虚拟机,虚拟机的设置中,找到网络选项卡,然后点击网络 ...

  9. C后端设计开发 - 第7章-真气-遗失的网络IO

    正文 第7章-真气-遗失的网络IO 后记 如果有错误, 欢迎指正. 有好的补充, 和疑问欢迎交流, 一块提高. 在此谢谢大家了. ボクらの冒険 : http://music.163.com/#/m/s ...

  10. jq监听ajax执行开始,出错,结束。

    $(“#msg”).ajaxComplete(function(event,request, settings){   $(this).append(“<li>请求完成.</li&g ...