hdu 1402(FFT乘法 || NTT乘法)
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
Note: the length of each integer will not exceed 50000.
就一个高精度乘法 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乘法)的更多相关文章
- HDU 1402 FFT 大数乘法
$A * B$ FFT模板题,找到了一个看起来很清爽的模板 /** @Date : 2017-09-19 22:12:08 * @FileName: HDU 1402 FFT 大整数乘法.cpp * ...
- HDU 1402 fft 模板题
题目就是求一个大数的乘法 这里数字的位数有50000的长度,按平时的乘法方式计算,每一位相乘是要n^2的复杂度的,这肯定不行 我们可以将每一位分解后作为系数,如153 = 1*x^2 + 5*x^1 ...
- hdu 1402 FFT(模板)
A * B Problem Plus Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Other ...
- 多项式乘法,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 ...
- hdu 1402 A * B Problem Plus FFT
/* hdu 1402 A * B Problem Plus FFT 这是我的第二道FFT的题 第一题是完全照着别人的代码敲出来的,也不明白是什么意思 这个代码是在前一题的基础上改的 做完这个题,我才 ...
- 洛谷P3803 【模板】多项式乘法 [NTT]
题目传送门 多项式乘法 题目描述 给定一个n次多项式F(x),和一个m次多项式G(x). 请求出F(x)和G(x)的卷积. 输入输出格式 输入格式: 第一行2个正整数n,m. 接下来一行n+1个数字, ...
- hdu 5187 高速幂高速乘法
http://acm.hdu.edu.cn/showproblem.php?pid=5187 Problem Description As one of the most powerful brush ...
- HDU 3074.Multiply game-区间乘法-线段树(单点更新、区间查询),上推标记取模
Multiply game Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Tot ...
- 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 ...
随机推荐
- HDU 1728 逃离迷宫 (广搜)
题目链接 Problem Description 给定一个m × n (m行, n列)的迷宫,迷宫中有两个位置,gloria想从迷宫的一个位置走到另外一个位置,当然迷宫中有些地方是空地,gloria可 ...
- 关于Redis在Linux手动安装配置
安装: 1.获取redis资源 wget http://download.redis.io/releases/redis-5.0.0.tar.gz 2.解压 tar xzvf redis-5.0.0. ...
- javascript继承机制 & call apply使用说明
一.继承机制 1.对象冒充:构造函数使用 this 关键字给所有属性和方法赋值,可使 ClassA 构造函数成为 ClassB 的方法,然后调用它. function ClassZ() { this. ...
- vue手势解决方案
1.需求 因为项目中要做一个可以移动.旋转和放缩具有合成图片的功能,例如: 剑可以随意移动,然后把位移.旋转角度和放缩值传给后台进行合成. 2.解决方案 网上搜到手势插件AlloyFinger,htt ...
- HTTPS加密通信原理及数字证书系统
https加密通信原理: 公钥私钥成对,公钥公之于众,私钥只有自己知道. 用公钥加密的信息只能由与之相对应的私钥解密. 甲给乙发送数据时,甲先用乙的公钥加密这段数据,再用自己的私钥对这段数据的特征数据 ...
- python中requests库中文乱码问题
当使用这个库的时候经常会出现各种乱码的情况. 首先要知道: text返回的是处理过的unicode的数据. content返回的是bytes的原始数据 也就是说r.content比r.text更加节省 ...
- 【DLL】动态库的创建,隐式加载和显式加载(转)
原文转自:https://blog.csdn.net/dcrmg/article/details/53437913
- centos 挂在ntfs
Installing build-essentials in CentOS (make, gcc, gdb):http://www.techblogistech.com/2012/03/install ...
- 在 Visual Studio 中使用正则表达式
Visual Studio 使用 .NET framework 正则表达式查找和替换文本. 在 Visual Studio 2010 和早期版本中,Visual Studio 在“查找和替换”窗口中使 ...
- Leetcode 之Count and Say(35)
很有意思的一道题,不好想啊. string getNext(string &s) { ]; ; stringstream ss; ; i < s.size(); i++) { if (s ...