UOJ#34 多项式乘法(FFT模板题)

题目描述

给你两个多项式,请输出乘起来后的多项式。

输入格式

第一行两个整数  n n 和  m m,分别表示两个多项式的次数。

第二行  n+1 n+1 个整数,分别表示第一个多项式的  0 0 到  n n 次项前的系数。

第三行  m+1 m+1 个整数,分别表示第一个多项式的  0 0 到  m m 次项前的系数。

输出格式

一行  n+m+1 n+m+1 个整数,分别表示乘起来后的多项式的  0 0 到  n+m n+m 次项前的系数。

样例一

input
1 2
1 2
1 2 1

output
1 4 5 2

explanation

(1+2x)⋅(1+2x+x2)=1+4x+5x2+2x3

限制与约定


0≤n,m≤105
,保证输入中的系数大于等于  0  且小于等于  9

时间限制 1s

空间限制 256MB



题解:FFT模板

递归版本

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define N 2300003
#define pi acos(-1)
using namespace std;
struct data{
	double x,y; 
	data (double X=0,double Y=0) {
		x=X,y=Y;
	}
}a[N],b[N],c[N];
data operator +(data a,data b){  return data(a.x+b.x,a.y+b.y); }
data operator -(data a,data b){  return data(a.x-b.x,a.y-b.y); }
data operator *(data a,data b){  return data(a.x*b.x-a.y*b.y,a.y*b.x+b.y*a.x); }
int n,m;
char s[N];
void fft(data x[N],int n,int opt)//求解出的是系数向量的离散傅里叶变换,y=(y0,y1,...,yn-1) ,其中yk=A(wn^k)=sigma(j=0..n-1)aj*wn^kj 
{
	if (n==1) return;//一个元素DFT就是他本身,y0=a0*W1^0=a0*1=a0 
	data l[n>>1],r[n>>1];
	for (int i=0;i<n;i+=2)
	 l[i>>1]=x[i],r[i>>1]=x[i+1];//A[0](x)=a0+a2*x+a4*x^2+..+an-2*x^(n/2-1)
	 //A[1](x)=a1+a3*x+a5*x^2+..+an-1*x^(n/2-1)
	 //A(x)=A[0](x^2)+x*A[1](x^2)
	 //所以求A(x)在wn^0,wn^1,....,wn^(n-1)处值的问题就转换成了求次数界n/2的多项式A[0](x),A[1](x)在点(wn^0)^2,(wn^1)^2,...,(wn^(n-1))^2的取值
	 //对于y0,y1,..,yn/2-1,yk=yk[0]+wn^k*yk[1]=A[0](wn^2k)+wn^k*A[1](wn^2k),根据消去引理wn^2k=w(n/2)^k ,yk=A(wn^k)
	 //对于yn/2,yn/2+1,..yn-1, yk+n/2=yk[0]-wn^k*yk[1]  wn^(k+n/2)=wn^k*wn^(n/2)其中wn^(n/2)=w2=-1 
	 //yk+n/2=yk[0]-wn^k*yk[1]=yk[0]-wn^(k+n/2)*yk[1]=A[0](wn^2k)+wn^(k+n/2)*A[1](wn^2k) 
	 //根据wn^(2k+n)=wn^2k ,得 yk+n/2= A[0](wn^(2k+n))+wn^(k+n/2)*A[1](wn^(2k+n))=A(wn^(k+n/2))
	fft(l,n>>1,opt); fft(r,n>>1,opt);
	data wn=data(cos(2*pi/n),sin(opt*2*pi/n));//主n次单位根,其他n次单位复数根都是wn的幂次 
	//如果opt==-1,那么我们实际是求的是逆DFT,把a与y互换,用wn^(-1)替换wn 
	data w=data(1,0),t;//wn^0=1 
	for (int i=0;i<n>>1;i++,w=w*wn)
	 t=w*r[i],x[i]=l[i]+t,x[i+(n>>1)]=l[i]-t;
}
int main()
{
	freopen("a.in","r",stdin);
	scanf("%d%d",&n,&m);
	for (int i=0;i<=n;i++) scanf("%lf",&a[i].x);
	for (int i=0;i<=m;i++) scanf("%lf",&b[i].x);
	m=n+m; //相乘后次数界变成两个多项式次数界的和 
	for (n=1;n<=m;n<<=1);
	fft(a,n,1); fft(b,n,1);
	for (int i=0;i<=n;i++) c[i]=a[i]*b[i];//这里得到的是点值表达 
	fft(c,n,-1);//aj=1/n*sigma(k=0..n-1)yk*wn^(-kj) 
	for (int i=0;i<=m;i++) printf("%d ",(int)(c[i].x/n+0.5));
	printf("\n");
}

非递归版本

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 2300003
#define pi acos(-1)
using namespace std;
struct data{
	double x,y; 
	data (double X=0,double Y=0) {
		x=X,y=Y;
	}
}a[N],b[N],c[N];
data operator +(data a,data b){  return data(a.x+b.x,a.y+b.y); }
data operator -(data a,data b){  return data(a.x-b.x,a.y-b.y); }
data operator *(data a,data b){  return data(a.x*b.x-a.y*b.y,a.y*b.x+b.y*a.x); }
int n,m,L,R[N];
char s[N];
void fft(data a[N],int opt)
{
	for (int i=0;i<n;i++) 
	 if (i<R[i]) swap(a[i],a[R[i]]);
	for(int i=1;i<n;i<<=1){
		data wn=data(cos(pi/i),opt*sin(pi/i));
		for (int p=i<<1,j=0;j<n;j+=p) {
			data w=data(1,0);
			for (int k=0;k<i;k++,w=w*wn){
				data x=a[j+k],y=w*a[j+k+i];
				a[j+k]=x+y; a[j+k+i]=x-y;
			}
		}
	}
}
int main()
{
	freopen("a.in","r",stdin);
	scanf("%d%d",&n,&m);
	for (int i=0;i<=n;i++) scanf("%lf",&a[i].x);
	for (int i=0;i<=m;i++) scanf("%lf",&b[i].x);
	m=n+m;
	for (n=1;n<=m;n<<=1) L++;
    for (int i=0;i<n;i++)
	 R[i]=(R[i>>1]>>1)|((i&1)<<(L-1));//位逆序置换 
	fft(a,1); fft(b,1);
	for (int i=0;i<=n;i++) a[i]=a[i]*b[i];
	fft(a,-1);
	for (int i=0;i<=m;i++)
	 printf("%d ",(int)(a[i].x/n+0.5));
	printf("\n");
} 





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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值