【Bluestein's Algorithm】[POJ2821]TN's Kingdom III - Assassination

题目大意

TN要暗杀Dzx,为了保密,他想到了这样一种方式:首先,把信息编码为N个实数,组成序列α,之后再随便搞一个长度为N的实数序列β。然后按照下面的步骤计算序列γ:
0、做一个空序列γ。
1、把β倒过来。
2、把β向右平移一个元素。最右侧的元素补到左边。
3、计算此时α和β对应元素的积的和。将其加到γ的末尾。
4、如果γ还不足N个元素,重复步骤2和3。

虽然这种加密方法是很弱的,可是那些笨蛋刺客们却没法破解。然后,这些东西就被Dzx拿到了,于是他就躲过了暗杀……现在作为历史学家的你得到了β和γ,你需要解出α来获得研究TN的材料。

分析

说得更直白一点,γ就是αβ的循环卷积。

运用FFT所进行的卷积本身就是循环卷积

我们假设cab两个序列的卷积,即ck=ki=0ak×bki
令n为大于等于c的长度而且是2的整数次幂的数,那么卷积也可以写成这个形式ck=i,jakbj[i+j0(modn)]
FFT就是根据第二个式子算出来的,当a,b长度相等而且n等于他们的长度的时候,根据这个式子算出来的就是循环卷积。平时计算的时候由于nc的长度,所以和线性卷积的式子没有区别。
很显然,我们对γβDFT,然后相除,再IDFT即可,但是用FFT进行DFT要求长度必须是2的整数次幂,如果原长不是2的整数次幂,我们强行弄成2的整数次幂的话,在做γDFT和最后IDFT时就会出现问题,因为你根本不知道你这样做完了是一个什么东西。
所以,我们需要一个可以做任意长度卷积的东西,Bluesteins  Algorithm或者混合基FFT

Bluestein’s Algorithm

我们考虑DFT的式子

AkjkAk=j=0N1ajωjkN=j=0N1aje2πiNjk=(jk)2+j2+k22=j=0N1ajeπiN((jk)2+j2+k2)=eπiNkajeπiNjeπiN((kj)2)

Xj=ajeπiNj,Yj=eπiN(kj)2

Ak=eπiNkj=0NXjYkj

这是一个卷积的形式,我们发现我们可以通过一次卷积来做一次DFT.
ps:其实是CZT,fft是dft的快速算法,其实就是N点dft算法,就是计算量小一点。N点dft的本质是z变换后,在z域单位圆上等间距N点连续采样。
IDFT的时候,可以类比用FFT进行IDFT时做出的改变即可。
我们发现kj可能小于0,我们只需要将Y(x)右移N位即可。

代码

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const double pi=acos(-1);
#define MAXN (1<<17)
int n;
struct cpx{
    double r,i;
    inline cpx(){
    }
    inline cpx(double r,double i=0):r(r),i(i){
    }
    inline cpx operator+(const cpx &b)const{
        return cpx(r+b.r,i+b.i);
    }
    inline cpx operator-(const cpx &b)const{
        return cpx(r-b.r,i-b.i);
    }
    inline cpx operator*(const cpx &b)const{
        return cpx(r*b.r-i*b.i,r*b.i+i*b.r);
    }
    inline cpx operator/(const cpx &b)const{
        return cpx((r*b.r+i*b.i)/(b.r*b.r+b.i*b.i),(i*b.r-r*b.i)/(b.r*b.r+b.i*b.i));
    }
    inline cpx& operator*=(const cpx &b){
        return *this=*this*b;
    }
    inline cpx &operator/=(const cpx &b){
        return *this=*this/b;
    }
}beta [MAXN+10],gamma[MAXN+10],alpha[MAXN+10],A[MAXN*4+10],B[MAXN*4+10];
void fft(cpx *a,int N,int f){
    int i,j,t,k;
    for(i=1,j=0;i<N-1;i++){
        for(t=N;j^=(t>>=1),~j&t;);
        if(i<j)
            swap(a[i],a[j]);
    }
    for(i=1;i<N;i<<=1){
        cpx wn(cos(pi/i),f*sin(pi/i));
        t=i<<1;
        for(j=0;j<N;j+=t){
            cpx w(1,0);
            for(k=0;k<i;k++,w*=wn){
                cpx x(a[j+k]),y(w*a[j+i+k]);
                a[j+k]=x+y;
                a[j+i+k]=x-y;
            }
        }
    }
    if(f==-1){
        for(i=0;i<N;i++)
            a[i]/=N;
    }
}
void bluestein(cpx *a,int n,int f){
    int N,i;
    memset(A,0,sizeof A);
    memset(B,0,sizeof B);
    for(i=0;i<n;i++)
        A[i]=cpx(cos(pi*i*i/n),f*sin(pi*i*i/n))*a[i];
    for(i=0;i<(n<<1);i++)
        B[i]=cpx(cos(pi*(i-n)*(i-n)/n),-f*sin(pi*(i-n)*(i-n)/n));
    for(N=1;N<(n<<2);N<<=1);
    fft(A,N,1);
    fft(B,N,1);
    for(i=0;i<N;i++)
        A[i]*=B[i];
    fft(A,N,-1);
    for(i=0;i<n;i++){
        a[i]=A[i+n]*cpx(cos(pi*i*i/n),f*sin(pi*i*i/n));
        if(f==-1)
            a[i]/=n;
    }
}
void read(){
    scanf("%d",&n);
    int i;
    for(i=0;i<n;i++)
        scanf("%lf",&beta[i].r);
    for(i=0;i<n;i++)
        scanf("%lf",&gamma[i].r);
}
void solve(){
    bluestein(beta,n,1);
    bluestein(gamma,n,1);
    for(int i=0;i<n;i++)
        alpha[i]=gamma[i]/beta[i];
    bluestein(alpha,n,-1);
}
void print(){
    int i;
    for(i=0;i<n;i++)
        printf("%.4f\n",alpha[i].r);
}
int main()
{
    read();
    solve();
    print();
}
posted @ 2016-09-07 15:18  outer_form  阅读(403)  评论(0编辑  收藏  举报