任意模数FFT(拆系数FFT)

作用

求多项式卷积对 p p p 取模的结果, p p p 不一定能写成 2 k + 1 2^k+1 2k+1 的形式。

做法

菜鸡不会三模数只好拆系数

将两个多项式拆开乘就不会乘爆,最后加一下取个模就行了。

把多项式 ( A   和   B ) (A\ 和\ B) (A  B) 的系数拆成 a i = A i > > 15    ,    b i = A i & 32767    , c i = B i > > 15    ,    d i = B i & 32767 a_{i}=A_{i}>>15\ \ ,\ \ b_{i}=A_{i}\& 32767\ \ ,c_{i}=B_{i}>>15\ \ ,\ \ d_{i}=B_{i}\& 32767 ai=Ai>>15  ,  bi=Ai&32767  ,ci=Bi>>15  ,  di=Bi&32767.

然后分别乘一下, F = a ∗ c    ,    G = a ∗ b + c ∗ d    ,    H = b ∗ d F=a*c\ \ ,\ \ G=a*b+c*d\ \ ,\ \ H=b*d F=ac  ,  G=ab+cd  ,  H=bd.

最后答案就是 : F < < 30 + G < < 15 + H F<<30+G<<15+H F<<30+G<<15+H 对于每一项先取模再右移,可以保证不爆。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 300005
#define LL long long 
#define LB long double 
using namespace std;

int mod;
int n,m,bit,tot,rev[N];
const LB pi=3.1415926535897932384626433832795028841;
struct complex
{
	LB x,y;
	complex operator+ (const complex &t) const 
	{
		return (complex){x+t.x,y+t.y};
	}
	complex operator- (const complex &t) const
	{
		return (complex){x-t.x,y-t.y};
	}
	complex operator* (const complex &t) const 
	{
		return (complex){x*t.x-y*t.y,x*t.y+y*t.x};
	}
}a[N],b[N],c[N],d[N],f[N],g[N],h[N];

inline int qr()
{
	char a=0;int w=1,x=0;
	while(a<'0'||a>'9'){if(a=='-')w=-1;a=getchar();}
	while(a<='9'&&a>='0'){x=(x<<3)+(x<<1)+(a%48);a=getchar();}
	return x*w;
}

void init_rev(int len)
{
	while((1<<bit)<len)
		bit++;
	tot=(1<<bit);
	for(register int i=0;i<tot;i++)
		rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));
}

inline void FFT(complex *a,int opt)
{
	for(register int i=0;i<tot;i++)
		if(i<rev[i])
			swap(a[i],a[rev[i]]);
	for(register int mid=1;mid<tot;mid<<=1)
	{
		complex w1=(complex){cos(pi/(LB)mid),(LB)opt*sin(pi/(LB)mid)};
		for(register int i=0;i<tot;i+=mid<<1)
		{
			complex wk=(complex){1,0};
			for(register int j=0;j<mid;j++)
			{
				complex x=a[i+j];
				complex y=wk*a[i+j+mid];
				a[i+j]=x+y;
				a[i+j+mid]=x-y;
				wk=wk*w1;
			}
		}
	}
	if(opt==-1)
	{
		LB op=1.00000/(LB)tot;
		for(register int i=0;i<tot;i++)
			a[i].x=(LL)(a[i].x*op+0.5);
	}
}

int main()
{
	n=qr();
	m=qr();
	mod=qr();
	init_rev(m+n+1);
	for(register int i=0;i<=n;i++)
	{
		int x=qr();
		a[i].x=x>>15;
		b[i].x=x&32767;
	}
	for(register int i=0;i<=m;i++)
	{
		int x=qr();
		c[i].x=x>>15;
		d[i].x=x&32767;
	}
	FFT(a,1);
	FFT(b,1);
	FFT(c,1);
	FFT(d,1);
	for(register int i=0;i<tot;i++)
	{
		f[i]=a[i]*c[i];
		g[i]=a[i]*d[i]+b[i]*c[i];
		h[i]=b[i]*d[i];
	}
	FFT(f,-1);
	FFT(g,-1);
	FFT(h,-1);
	for(register int i=0;i<n+m+1;i++)
	{
		LL op1=((LL)(f[i].x)%mod)<<30ll;
		LL op2=((LL)(g[i].x)%mod)<<15ll;
		LL op3=(LL)(h[i].x)%mod;
		printf("%lld ",(op1+op2+op3)%mod);
	}
	printf("\n");
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值