HDU1402 A * B Problem Plus 快速傅里叶变换FFT

看了算法导论(第2版)第30章的《多项式与快速傅里叶变换》

多项式有系数表示法和点值表示法。

两个次数界为n的多项式A(x)和B(x)相乘,输入输出均采用系数表示法。(假定n为2的幂)

1)使次数界增加一倍:A(x)和B(x)扩充为次数界为2n的多项式,并构造起系数表示

2)求值:两次应用2n阶FFT,计算出A(x)和B(x)的长度为2n的点值表示

3)点乘:计算多项式C(x)=A(x)*B(x)的点值表示

4)插值:对2n个点值对应用一次FFT计算出其逆DFT,就可以构造出多项式C(x)的系数表示

第1步和第3步需要时间为O(n),第2步和第4步运用FFT需要时间为O(nlgn)。

第2步求取n个点的值所需时间为O(n^2),如果我们巧妙地选取x(k)则其运行时间变为O(nlgn),FFT主要利用了单位复根(w^n=1的w就是n次单位复根,他们是e^(2PI*k/n),k=0,1,……n-1)的特殊性质。

用FFT做SPOJ235竟然TLE,传说要用NTT(数论变换number theoretic transform),有待提高ing

Accepted 1402 375MS 6740K 3099 B
 
  
#include < iostream >
#include
< cmath >
using namespace std;
const double PI = acos( - 1.0 );
struct vir{
double re,im; // 实部和虚部
vir( double a = 0 , double b = 0 ){
re
= a; im = b;
}
vir
operator + ( const vir & b){
return vir(re + b.re,im + b.im);
}
vir
operator - ( const vir & b){
return vir(re - b.re, im - b.im);
}
vir
operator * ( const vir & b){
return vir(re * b.re - im * b.im , re * b.im + im * b.re);
}
};
vir x1[
200005 ],x2[ 200005 ];
void change(vir * x, int len, int loglen)
{
int i,j,k,t;
for (i = 0 ;i < len;i ++ )
{
t
= i;
for (j = k = 0 ; j < loglen; j ++ ,t >>= 1 )
k
= (k << 1 ) | (t & 1 );
if (k < i){
vir wt
= x[k];
x[k]
= x[i];
x[i]
= wt;
}
}
}
void fft(vir * x, int len, int loglen)
{
int i,j,t,s,e;
change(x,len,loglen);
t
= 1 ;
for (i = 0 ;i < loglen;i ++ ,t <<= 1 )
{
s
= 0 ; e = s + t;
while (s < len)
{
vir a,b,wo(cos(PI
/ t),sin(PI / t)),wn( 1 , 0 );
for (j = s;j < s + t;j ++ )
{
a
= x[j]; b = x[j + t] * wn;
x[j]
= a + b; x[j + t] = a - b;
wn
= wn * wo;
}
s
= e + t; e = s + t;
}
}
}
void dit_fft(vir * x, int len, int loglen)
{
int i,j,s,e,t = 1 << loglen;
for (i = 0 ;i < loglen;i ++ )
{
t
>>= 1 ;
s
= 0 ; e = s + t;
while (s < len)
{
vir a,b,wn(
1 , 0 ),wo(cos(PI / t), - sin(PI / t));
for (j = s;j < s + t;j ++ )
{
a
= x[j] + x[j + t]; b = (x[j] - x[j + t]) * wn;
x[j]
= a; x[j + t] = b;
wn
= wn * wo;
}
s
= e + t; e = s + t;
}
}
change(x,len,loglen);
for (i = 0 ;i < len;i ++ )
x[i].re
/= len;
}
int main()
{
char a[ 100005 ],b[ 100005 ];
int i,len1,len2,len,loglen;
int t,over;
while (scanf( " %s%s " ,a,b) != EOF)
{
len1
= strlen(a) << 1 ;
len2
= strlen(b) << 1 ;
len
= 1 ;
loglen
= 0 ;
while (len < len1)
len
<<= 1 , loglen ++ ;
while (len < len2)
len
<<= 1 , loglen ++ ;
// init
for (i = 0 ;a[i];i ++ )
x1[i].re
= a[i] - ' 0 ' , x1[i].im = 0 ;
for (;i < len;i ++ )
x1[i].re
= x1[i].im = 0 ;
for (i = 0 ;b[i];i ++ )
x2[i].re
= b[i] - ' 0 ' , x2[i].im = 0 ;
for (;i < len;i ++ )
x2[i].re
= x2[i].im = 0 ;
fft(x1,len,loglen);
fft(x2,len,loglen);
for (i = 0 ;i < len;i ++ )
x1[i]
= x1[i] * x2[i];
dit_fft(x1,len,loglen);

// 将x1.re从浮点数转化为十进制整型存入a
for (i = (len1 + len2) / 2 - 2 ,over = len = 0 ;i >= 0 ;i -- )
{
t
= x1[i].re + over + 0.5 ;
a[len
++ ] = t % 10 ;
over
= t / 10 ;
}
while (over){
a[len
++ ] = over % 10 ;
over
/= 10 ;
}

// output
for (len -- ;len >= 0 &&! a[len];len -- );
if (len < 0 )
putchar(
' 0 ' );
else
for (;len >= 0 ;len -- )
putchar(a[len]
+ ' 0 ' );
putchar(
' \n ' );
}
return 0 ;
}

 

转载于:https://www.cnblogs.com/DreamUp/archive/2010/09/05/1818576.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值