BZOJ 2194 快速傅里叶之二 题解

题目描述

求:\(c_k=\sum(a_i\times b_{i-k})\)

推导

一般卷积是和一定,然而这个是差一定,于是我们考虑把 \(b\) 反过来(其实反 \(a\) 也可以)。

\(c_k=\sum^{n-1}_{i=k}(a_i\times b_{n+k-i})\)

\(d_k=\sum^{n-1}_{i=k}(a_i\times b_{k-i})\)

\(c_k=d_{n+k}\)

\(d_k\) 的推导很像卷积,考虑将其变换形式,即 \(d_{n+k}=\sum^{n+k}_{i=0}(a_i \times b_{n+k-i})\)

至此,可以通过 FFT 来求解。

/*
 * Title: #2194. 快速傅立叶之二
 * Source: HydroOJ-BZOJ
 * URL: https://hydro.ac/d/bzoj/p/2194
 * Author: Steven_lzx
 * Command: -std=c++23 -Wall -fno-ms-extensions
 */
#include <bits/stdc++.h>
using namespace std;
typedef complex<double> cd;
const int MAXN=1<<20;
const double PI=acos(-1),TAU=2*PI;
cd a[MAXN],b[MAXN];
void FFT(cd *a,int n,int dft)
{
    cd w,wk,x,y;
    for(int i=0,k=0;i<n;i++)
    {
        if(i>k)
            swap(a[i],a[k]);
        for(int j=n>>1;(k^=j)<j;j>>=1);
    }
    for(int k=2;k<=n;k<<=1)
    {
        w=cd(cos(TAU*dft/k),sin(TAU*dft/k));
        for(int i=0;i<n;i+=k)
        {
            wk=cd(1,0);
            for(int j=0;j<(k>>1);j++)
            {
                x=a[i+j];
                y=wk*a[i+j+(k>>1)];
                a[i+j]=x+y;
                a[i+j+(k>>1)]=x-y;
                wk*=w;
            }
        }
    }
    return;
}
int main()
{
    int n,len;
    double x,y;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        scanf("%lf%lf",&x,&y);
        a[i]=cd(x,0);
        b[n-i-1]=cd(y,0);
    }
    for(len=1;len<=(n<<1);len<<=1);
    FFT(a,len,1);
    FFT(b,len,1);
    for(int i=0;i<len;i++)
    {
        a[i]*=b[i];
    }
    FFT(a,len,-1);
    for(int i=n-1;i<(n<<1)-1;i++)
        printf("%lld\n",(long long)(a[i].real()/len+0.1));
    return 0;
}
posted @ 2022-10-26 19:29  Day_Dreamer_D  阅读(27)  评论(0)    收藏  举报