HDU - 1402 A * B Problem Plus FFT裸题

http://acm.hdu.edu.cn/showproblem.php?pid=1402

题意:

  求$a*b$ 但是$a$和$b$的范围可以达到 $1e50000$

题解:

  显然...用字符串模拟的大数或者压位的大数是无法胜任这种计算的....

  然后,2个大整数相乘,可以理解为卷积,所以就用快速傅里叶变换(FFT)来加速他

  模板题

  简单总结一下对FFT的认知:

FFT用于算卷积,卷积可以理解为两个多项式相乘
显然复杂度是$O(n^2)$的 但是fft可以优化为$O(nlogn)$
如何优化,
考虑2个点确定直线,3个点确定抛物线,$n+1$个点确定了$n$次多项式
我们用$n+1$个点$(x_i,y_i)$表达了原来的式子
如何用点值表达卷积(多项式相乘)?
式子展开可以知道,$x_i$确定的时候,对于点值表达式就对应了点$(x_i,f(x_i)*g(x_i))$
也就是可以$O(1)$得到一个点答案,$n$个点就是$O(n)$
那问题又来了,$n+1$个点的$(x_i,y_i)$,每次求解一次多项式的值,需要$O(n)$
也就是$O(n^2)$复杂度,依然超时
所以,现在考虑如何快速的算多项式的值
因为$x_i$我们可以自己定义,因此利用虚根的特殊性质,我们就能分治处理了
就可以复杂度$O(nlogn)$的得到点值表达式
然后$O(n)$的算点值表达式的值
然后逆运算回到答案

#include <bits/stdc++.h>
#define ll long long
#define usd unsigned
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
#define rep(ii,a,b) for(int ii=a;ii<=b;++i)
#define per(ii,a,b) for(int ii=b;ii>=a;--i)
#define show(x) cout<<#x<<"="<<x<<endl
#define show2(x,y) cout<<#x<<"="<<x<<" "<<#y<<"="<<y<<endl
#define show3(x,y,z) cout<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl
#define show4(w,x,y,z) cout<<#w<<"="<<w<<" "<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl
#define showa(a,b) cout<<#a<<‘[‘<<b<<"]="<<a[b]<<endl
#define pii pair<int,int>
#define cp complex<double>//<complex>头文件里的东西
using namespace std;
const int maxn=2e5+10;
const int maxm=2e6+10;
const int INF=0x3f3f3f3f;
const int mod=1e9+7;
int casn,n,m,k;
const double pi=acos(-1.0);
int lena,lenb,len,pw,ans[maxn],rr[maxn];
cp a[maxn] ,b[maxn];
char sa[maxn],sb[maxn];
void fft(cp *a,int f){//递归分治太慢,用迭代实现
	rep(i,0,n-1) if(i<rr[i]) swap(a[i],a[rr[i]]);//分治原来的数组
	for(int i=1;i<n;i<<=1){
		cp wn(cos(pi/i),sin(f*pi/i)),x,y;//计算虚根
		for(int j=0;j<n;j+=(i<<1)){
			cp w(1,0);
			for(int k=0;k<i;++k,w*=wn){
				x=a[j+k],y=w*a[i+j+k];
				a[j+k]=x+y;
				a[i+j+k]=x-y;
			}
		}
	}
}
int main(){
	while(~scanf("%s%s",sa,sb)){
		n=1;
		len=pw=0;
		memset(ans,0,sizeof ans);
		lena=strlen(sa);
		lenb=strlen(sb);
		while(n<=lena+lenb) n<<=1,++pw;
		rep(i,0,lena-1) a[i]=sa[lena-i-1]-‘0‘;
		rep(i,lena,n) a[i]=0;
		rep(i,0,lenb-1) b[i]=sb[lenb-i-1]-‘0‘;
		rep(i,lenb,n) b[i]=0;
		rep(i,0,n) rr[i]=(rr[i>>1]>>1)|((i&1)<<(pw-1));//分治中的二进制规律,因此预处理参数
		fft(a,1);//DFT a
		fft(b,1);//DFT b
		rep(i,0,n) a[i]*=b[i];
		fft(a,-1);//IDFT a*b
		rep(i,0,lena+lenb) {
			ans[i]+=int(a[i].real()/n+0.5);
			ans[i+1]+=ans[i]/10;
			ans[i]%=10;
			if(ans[i]>0) len=i;
		}
		per(i,0,len){
			putchar (ans[i]+‘0‘);
		}
		puts("");
	}
  return 0;
}

  

原文地址:https://www.cnblogs.com/nervendnig/p/9490286.html

时间: 2024-10-09 06:37:18

HDU - 1402 A * B Problem Plus FFT裸题的相关文章

HDU 1402 A * B Problem Plus FFT

A * B Problem Plus 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

hdu 1402 大数A*B模板(FFT)

hdu 1402 大数A*B模板(FFT) 题目链接 参考博客 #include <cstdio> #include <cmath> #include <cstring> #include <algorithm> #include <complex> #include <iostream> using namespace std; typedef long long ll; template <class T> void

HDU 5832 A water problem(某水题)

HDU 5832 A water problem(某水题) Time Limit: 5000/2500 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)   Problem Description - 题目描述 Two planets named Haha and Xixi in the universe and they were created with the universe beginning. There is

HDU 4143 A Simple Problem(数论-水题)

A Simple Problem Problem Description For a given positive integer n, please find the smallest positive integer x that we can find an integer y such that y^2 = n +x^2. Input The first line is an integer T, which is the the number of cases. Then T line

HDU 1402 A * B Problem Plus ——(大数乘法,FFT)

因为刚学fft,想拿这题练练手,结果WA了个爽= =. 总结几点犯的错误: 1.要注意处理前导零的问题. 2.一定要注意数组大小的问题.(前一个fft的题因为没用到b数组,所以b就没管,这里使用了b数组,结果忘记给其大小乘以4倍了) 代码如下: 1 #include<bits/stdc++.h> 2 using namespace std; 3 const double pi = atan(1.0)*4; 4 const int N = 5e4 + 5; 5 typedef long long

HDU 1402 A * B Problem Plus(快速傅里叶变换)

题意:两个数相乘,每个数的长度不超过10^5: 思路:FFT第一题.通过将系数表达式转换为点值表达式,降低复杂度:算导是个好东西!!! 用DFT实现单位复根计算点值表达式,逆DFT则将点值表达式转为系数表达式,即计算插值:复杂度均为O(n^2); FFT采用分治的思想,将奇偶分开处理,优化DFT:复杂度为O(nlogn); FFT处理时,(将点值扩展到2^n)->(计算点值)->(点乘运算)->(计算插值); 由于奇偶分治时结果的位置发生变化,需要在傅里叶变换时进行二进制位置互换: 每经

hdu 2993 MAX Average Problem(斜率DP入门题)

题目链接:hdu 2993 MAX Average Problem 题意: 给一个长度为 n 的序列,找出长度 >= k 的平均值最大的连续子序列. 题解: 这题是论文的原题,请参照2004集训队论文<周源--浅谈数形结合思想在信息学竞赛中的应用> 这题输入有点大,要加读入优化才能过. 1 #include<bits/stdc++.h> 2 #define F(i,a,b) for(int i=a;i<=b;++i) 3 using namespace std; 4 5

hdu 1402 A * B Problem Plus (FFT + 大数相乘)

A * B Problem Plus Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 13456    Accepted Submission(s): 2401 Problem Description Calculate A * B. Input Each line will contain two integers A and B.

FFT(快速傅立叶变换):HDU 1402 A * B Problem Plus

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 唉,模板题,膜的邝