呜呜呜不要来北理上学
写在前面的话:
- 没有啦,想打ACM反正迟早都是要学的。
- 废寝忘食苦学了两天,终于照着板子+魔鬼调试敲过了大整数乘法。
- 真的好难,还好我们会用板子就可以啦。
- 看了许多BLOG,将真正写的非常好很有帮助的贴出来:
Leo_h1104
GGN_2015
GGN_2015
Duan2baka
路人黑的纸巾
Leo先生写的真是太棒啦!
本文是我给自己做个总结,由于讲的好的BLOG远少于讲的差的,我也不一定能讲好,各位看上面这些就够了。 - 呜呜呜不会用LaTex,就手写啦,尽量写的好看吼。
- 一定要看先知道数学原理,才能看懂代码。
一:数学:
- 先要明确,FFT是要将多项式的系数表达法转为点值表达法。至于为什么/什么意思,下面并不提及,需要您看上面的BLOG。如我所说,本文的定位是总结。
板子:
- 容器、定义
const int maxn = 1e5 + 5;
typedef complex<double> cd;
const double PI = 3.1415926535;
cd A[maxn * 5] , B[maxn * 5];//两个多项式的系数点集,DFT后转成点值点集,IDFT后转回系数点集
int rev[maxn * 5];
int MulAns[maxn * 5];//存结果的系数集
- 每个位置分治后的最终位置是它二进制翻转后得到的位置,用于迭代
void GetRev(int bit){
memset(rev , 0 , sizeof(rev));
for(int i=0;i<(1<<bit);i++)
rev[i] = (rev[i>>1]>>1) | ((i&1)<<(bit-1));
return ;
}
- DFT or IDFT
//数组名、2^、DFT/IDFT
void FFT(cd *a,int ran,int dft){
//dft=1:正变换;dft=-1:逆变换
for(int i=0;i<ran;i++)
if(i < rev[i])
swap(a[i] , a[rev[i]]);
for(int step=1;step<ran;step<<=1){
//自底向上模拟分治
//一共有len个系数
cd Wn = exp(cd(0 , dft*PI/step));//e^(i * (2PI/N));//N=2*step
for(int i=0;i<ran;i+=(step<<1)){
//针对本层的所有块分别处理,每次两块
cd Wnk(1 , 0);
for(int j=i;j<i+step;j++){
//蝴蝶变幻
cd l=a[j];
cd r=Wnk*a[j+step];
a[j] = l+r;
a[j+step] = l-r;
Wnk *= Wn;
}
}
}
if(dft == -1)
for(int i=0;i<ran;i++)
a[i]/=ran;
return ;
}
- 大整数乘法
void MUL(){
memset(A , 0 , sizeof(A));
memset(B , 0 , sizeof(B));//补零,最重要的初始化!
memset(MulAns , 0 , sizeof(MulAns));
int bit=1;//ran = 2^bit
int ran=(1<<1);//range;比如结果有1e4+5位,那么ran就是大于1e4+5且是2^bit。
for(bit=1 ; ran<len1+len2; bit++)
ran<<=1;
for(int i=0;i<len1;i++)
A[i] = (double)(L[len1-i-1] - '0');//顺便reverse
for(int i=0;i<len2;i++)
B[i] = (double)(R[len2-i-1] - '0');
GetRev(bit);
FFT(A,ran,1) , FFT(B,ran,1);//DFT
for(int i=0;i<ran;i++)
A[i] *= B[i];
FFT(A,ran,-1);//IDFT
for(int i=0;i<ran;i++){
MulAns[i] += (int)(A[i].real() + 0.5);//精度
MulAns[i+1] += MulAns[i]/10;
MulAns[i] %= 10;
}
int i;
for(i=ran-1;i>=0;i--)
if(MulAns[i])
break;
else
;
if(i < 0)
printf("0");
else
for(i;i>=0;i--)
printf("%d" , MulAns[i]);
printf("\n");
return ;
}