整理了一下目前所写过的fft题目(使用kuangbin模板)
首先需要明白一个概念,fft是用来求卷积的,他可以把离散的xx变成连续的xx,然后把n^2的时间复杂度变成nlogn。
比如现在计算28*76的值就可以使用卷积计算。
第一步:先把76逆置成67
第二步:
28
67 得8*6=48,放入a数组,a[0]=48
第三步:
28
67 得2*6+8*7=68,a[1]=68
第四步:
28
67 得2*7=14,a[2]=14
第五步:
a数组:14 68 48
个位为48%10=8,进4
十位为(68+4)%10=2,进7
百位为(14+7)%10=1,进2
千位为2
值为2128
1.hdu1402
大致题意:求a*b
测试案例:
input:
1
2
1000
2
output:
2
2000
解题思路:这题就是上面那两个一维卷积卷了得出的结果
代码:
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <math.h>
using namespace std;
const double PI = acos(-1.0);
//复数结构体
struct complex
{
double r,i;
complex(double _r = 0.0,double _i = 0.0)
{
r = _r; i = _i;
}
complex operator +(const complex &b)
{
return complex(r+b.r,i+b.i);
}
complex operator -(const complex &b)
{
return complex(r-b.r,i-b.i);
}
complex operator *(const complex &b)
{
return complex(r*b.r-i*b.i,r*b.i+i*b.r);
}
};
/*
* 进行FFT和IFFT前的反转变换。
* 位置i和 (i二进制反转后位置)互换
* len必须去2的幂
*/
void change(complex y[],int len)
{
int i,j,k;
for(i = 1, j = len/2;i < len-1; i++)
{
if(i < j)swap(y[i],y[j]);
//交换互为小标反转的元素,i<j保证交换一次
//i做正常的+1,j左反转类型的+1,始终保持i和j是反转的
k = len/2;
while( j >= k)
{
j -= k;
k /= 2;
}
if(j < k) j += k;
}
}
/*
* 做FFT
* len必须为2^k形式,
* on==1时是DFT,on==-1时是IDFT
*/
void fft(complex y[],int len,int on)
{
change(y,len);
for(int h = 2; h <= len; h <<= 1)
{
complex wn(cos(-on*2*PI/h),sin(-on*2*PI/h));
for(int j = 0;j < len;j+=h)
{
complex w(1,0);
for(int k = j;k < j+h/2;k++)
{
complex u = y[k];
complex t = w*y[k+h/2];
y[k] = u+t;
y[k+h/2] = u-t;
w = w*wn;
}
}
}
if(on == -1)
for(int i = 0;i < len;i++)
y[i].r /= len;
}
const int MAXN = 200010;
complex x1[MAXN],x2[MAXN];
char str1[MAXN/2],str2[MAXN/2];
int sum[MAXN];
int main()
{
while(scanf("%s%s",str1,str2)==2)
{
int len1 = strlen(str1);
int len2 = strlen(str2);
int len = 1;
while(len < len1*2 || len < len2*2)len<<=1;
for(int i = 0;i < len1;i++)
x1[i] = complex(str1[len1-1-i]-'0',0);
for(int i = len1;i < len;i++)
x1[i] = complex(0,0);
for(int i = 0;i < len2;i++)
x2[i] = complex(str2[len2-1-i]-'0',0);
for(int i = len2;i < len;i++)
x2[i] = complex(0,0);
//求DFT
fft(x1,len,1);
fft(x2,len,1);
for(int i = 0;i < len;i++)
x1[i] = x1[i]*x2[i];
fft(x1,len,-1);
for(int i = 0;i < len;i++)
sum[i] = (int)(x1[i].r+0.5);
for(int i = 0;i < len;i++)
{
sum[i+1]+=sum[i]/10;
sum[i]%=10;
}
len = len1+len2-1;
while(sum[len] <= 0 && len > 0)len--;
for(int i = len;i >= 0;i--)
printf("%c",sum[i]+'0');
printf("\n");
}
return 0;
}
2.hihocoder1388(2016 acm 北京网络赛e题)
大致题意:给出等长的A,B序列,求
测试案例:
input:
2
9
3 0 1 4 1 5 9 2 6
5 3 5 8 9 7 9 3 2
5
1 2 3 4 5
2 3 4 5 1
output:
80
0
解题思路:这题其实也是2个一维卷积的应用。公式可以化简为
此时我们可以把一个串变长一倍,再用另一个串反转求他们的卷积,如
1 2 3 4 5 1 2 3 4 5与2 3 4 5 1的卷积,然后就能看出有一串连续位置的fft值即为结果,求出其中最小值即可,又因为fft的精度问题,直接fft很可能WA,所以需要在
for(int i = 0;i < len;i++)
sum[i]= (int)(x1[i].r+0.5);
这种地方直接比较x1[i].r的大小,然后得出什么位置最小,重新按数组计算即可。
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
//复数结构体
struct complex
{
double r,i;
complex(double _r = 0.0,double _i = 0.0)
{
r = _r; i = _i;
}
complex operator +(const complex &b)
{
return complex(r+b.r,i+b.i);
}
complex operator -(const complex &b)
{
return complex(r-b.r,i-b.i);
}
complex operator *(const complex &b)
{
return complex(r*b.r-i*b.i,r*b.i+i*b.r);
}
};
/*
* 进行FFT和IFFT前的反转变换。
* 位置i和 (i二进制反转后位置)互换
* len必须去2的幂
*/
void change(complex y[],int len)
{
int i,j,k;
for(i = 1, j = len/2;i < len-1; i++)
{
if(i < j)swap(y[i],y[j]);
//交换互为小标反转的元素,i<j保证交换一次
//i做正常的+1,j左反转类型的+1,始终保持i和j是反转的
k = len/2;
while( j >= k)
{
j -= k;
k /= 2;
}
if(j < k) j += k;
}
}
/*
* 做FFT
* len必须为2^k形式,
* on==1时是DFT,on==-1时是IDFT
*/
void fft(complex y[],int len,int on)
{
change(y,len);
for(int h = 2; h <= len; h <<= 1)
{
complex wn(cos(-on*2*PI/h),sin(-on*2*PI/h));
for(int j = 0;j < len;j+=h)
{
complex w(1,0);
for(int k = j;k < j+h/2;k++)
{
complex u = y[k];
complex t = w*y[k+h/2];
y[k] = u+t;
y[k+h/2] = u-t;
w = w*wn;
}
}
}
if(on == -1)
for(int i = 0;i < len;i++)
y[i].r /= len;
}
const int maxn = 400010;
complex x1[maxn],x2[maxn];
ll a[maxn],b[maxn],num[maxn],sum;
int main()
{
int T,i,j,len,n,len1,len2;
scanf("%d",&T);
while (T--)
{
scanf("%d",&n);
sum=0;
len1=2*n;
len2=n;
for (i=0;i<n;i++)
{
scanf("%lld",&a[n-i-1]);
a[2*n-i-1]=a[n-i-1];
sum+=a[n-i-1]*a[n-i-1];
}
for (i=0;i<n;i++)
{
scanf("%lld",&b[n-i-1]);
sum+=b[n-i-1]*b[n-i-1];
}
len=1;
while (len<len1*2) len<<=1;
for (i=0;i<len1;i++)
{
x1[i]=complex(a[i],0);
}
for (i=len1;i<len;i++)
{
x1[i]=complex(0,0);
}
for (i=0;i<len2;i++)
{
x2[i]=complex(b[len2-i-1],0);
}
for (i=len2;i<len;i++)
{
x2[i]=complex(0,0);
}
fft(x1,len,1);
fft(x2,len,1);
for(int i = 0;i < len;i++)
x1[i] = x1[i]*x2[i];
fft(x1,len,-1);
ll ans=0;
double temp=-1;
int pos;
for(i = n-1;i < 2*n;i++)
{
if (temp<x1[i].r)
{
temp=x1[i].r;
pos=i;
}
}
int t=0;
for (i=pos;i>pos-n;i--)
{
ans+=a[i]*b[--len2];
}
printf("%lld\n",sum-2*ans);
}
return 0;
}