1. 程式人生 > 實用技巧 >Luogu4245 【模板】任意模數NTT

Luogu4245 【模板】任意模數NTT

https://www.luogu.com.cn/problem/P4245

拆係數\(FFT\)

把大數字拆成最後\(15\)位(二進位制下)和前面的位,直接\(FFT\),然後合併

\[(2^{15} a_i+b_i)(2^{15}c_i+d_i)=\\ 2^{30}a_i c_i+2^{15} (a_i d_i +b_i c_i)+b_i d_i \]

計算一下就好了

注意,本題對精度要求很高,儘量減少計算次數(一開始爆\(0\),還以為\(FFT\)炸了,傻了好久\(QAQ\)

最好用\(long \quad double\),雖然\(double\)可以過,執行較快,但還是很慌的(極容易\(WA\)

\(C++ Code:\)

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#define N 400005
#define D long double
#define ll long long
using namespace std;
int n,m,p,q,l,s,rev[N];
const D Pi=acos(-1.0);
ll g1,g2,g3,ans;
struct virt
{
    D x,y;
    virt (D xx=0.0,D yy=0.0)
    {
        x=xx,y=yy;
    }
    virt operator + (virt b)
    {
        return virt(x+b.x,y+b.y);
    }
    virt operator - (virt b)
    {
        return virt(x-b.x,y-b.y);
    }
    virt operator * (virt b)
    {
        return virt(x*b.x-y*b.y,x*b.y+y*b.x);
    }
}q1,q2,q3,q4,a[N],b[N],c[N],d[N],g[2][25],W[N];
void FFT(virt *a,int t)
{
	for (int i=0;i<s;i++)
		if (i<rev[i])
			swap(a[i],a[rev[i]]);
	for (int mid=1,o=1;mid<s;mid <<=1,o++)
		for (int j=0;j<s;j+=(mid << 1))
			for (int k=0;k<mid;k++)
			{
				virt x=a[j+k],y=W[mid+k]*a[j+k+mid];
				a[j+k]=x+y;
				a[j+k+mid]=x-y;
			}
    if (t==-1)
    {
        reverse(a+1,a+s);
        D k=1.0/s;
        for (int i=0;i<s;i++)
            a[i]=a[i]*k;
    }
}
int main()
{
    scanf("%d%d%d",&n,&m,&p);
    for (int i=0;i<=n;i++)
        scanf("%d",&q),a[i].x=q >> 15,b[i].x=q & ((1 << 15) - 1);
    for (int i=0;i<=m;i++)
        scanf("%d",&q),c[i].x=q >> 15,d[i].x=q & ((1 << 15) - 1);
    l=0,s=1;
    while (s<=n+m)
    {
        s <<=1;
        l++;
    }
    for (int i=0;i<s;i++)
        rev[i]=(rev[i >> 1] >> 1) | ((i & 1) << (l - 1));
    for(int i=1;i<s;i<<=1)
        for (int k=0;k<i;k++)
            W[i+k]=virt(cos(Pi*k/i),sin(Pi*k/i));
    FFT(a,1);
    FFT(b,1);
    FFT(c,1);
    FFT(d,1);
    for (int i=0;i<s;i++)
    {
        q1=a[i],q2=b[i],q3=c[i],q4=d[i];
        a[i]=q1*q3;
        b[i]=q1*q4+q2*q3;
        c[i]=q2*q4;
    }
    FFT(a,-1);
    FFT(b,-1);
    FFT(c,-1);
    for (int i=0;i<=n+m;i++)
    {
        g1=(ll)(a[i].x+0.5);
        g2=(ll)(b[i].x+0.5);
        g3=(ll)(c[i].x+0.5);
        g1=((g1%p) << 30)%p;
        g2=((g2%p) << 15)%p;
        g3=g3%p;
        ans=((g1+g2+g3)%p+p)%p;
        printf("%lld ",ans);
    }
    putchar('\n');
    return 0;
}