【HDU5730】Shell Necklace-CDQ分治+FFT(分治FFT)

测试地址:Shell Necklace
题目大意:一串链形贝壳项链(不是环形),有ai种方案装饰连续i个贝壳,问装饰整串项链有多少种方案。
做法:本题需要用到CDQ分治+FFT(分治FFT)。
首先令f(i)为装饰长为i的项链的方案数,特殊地,令f(0)=1,那么我们很快能得出状态转移方程:
f(i)=j=1if(ij)aj
直接计算这个方程是O(n2)的,不能接受。注意到等号右边的部分是一个卷积形式的式子,但是这个卷积和往常普通FFT可以做的卷积不同,它涉及到f自己和另一个数组的卷积,所以我们不能用普通的FFT来解决问题。
这时候分治FFT就出场了。分治FFT也可以写成CDQ分治+FFT。我们知道CDQ分治的主要思想就是对于每个区间[l,r],先递归计算区间[l,mid]中的答案,然后计算[l,mid][mid+1,r]中答案的贡献,最后递归计算区间[mid+1,r]。这里我们采用类似的过程,先算出区间[l,mid]中所有的f,然后将这一段fa做卷积,求出它们对区间[mid+1,r]中所有f的贡献,用FFT即可,最后计算区间[mid+1,r]。根据主定理,这个算法的时间复杂度是O(nlog2n)的,可以通过此题。
以下是本人代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=313;
const double pi=acos(-1.0);
int n,rev[400010];
ll A[100010],f[100010];
struct Complex
{
    double x,y;
}a[400010],b[400010];
Complex operator + (Complex a,Complex b) {Complex s={a.x+b.x,a.y+b.y};return s;}
Complex operator - (Complex a,Complex b) {Complex s={a.x-b.x,a.y-b.y};return s;}
Complex operator * (Complex a,Complex b) {Complex s={a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x};return s;}

void FFT(Complex *a,int type,int n)
{
    for(int i=0;i<n;i++)
        if (i<rev[i]) swap(a[i],a[rev[i]]);
    for(int mid=1;mid<n;mid<<=1)
    {
        Complex W={cos(pi/mid),type*sin(pi/mid)};
        for(int l=0;l<n;l+=(mid<<1))
        {
            Complex w={1.0,0.0};
            for(int k=0;k<mid;k++,w=w*W)
            {
                Complex x=a[l+k],y=w*a[l+mid+k];
                a[l+k]=x+y;
                a[l+mid+k]=x-y;
            }
        }
    }
    if (type==-1)
    {
        for(int i=0;i<n;i++)
            a[i].x/=(double)n;
    }
}

void solve(int l,int r)
{
    if (l==r) return;
    int mid=(l+r)>>1;

    solve(l,mid);

    int bit=0,x=1;
    while(x<((r-l+1)<<1)) x<<=1,bit++;
    for(int i=0;i<x;i++)
        a[i].x=a[i].y=b[i].x=b[i].y=0.0;
    for(int i=0;i<mid-l+1;i++)
        a[i].x=f[l+i];
    for(int i=0;i<r-l;i++)
        b[i].x=A[i+1];
    rev[0]=0;
    for(int i=1;i<x;i++)
        rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));
    FFT(a,1,x),FFT(b,1,x);
    for(int i=0;i<x;i++)
        a[i]=a[i]*b[i];
    FFT(a,-1,x);

    for(int i=mid+1;i<=r;i++)
    {
        f[i]+=a[i-l-1].x+0.5;
        f[i]%=mod;
    }

    solve(mid+1,r);
}

int main()
{
    while(scanf("%d",&n)&&n)
    {
        memset(f,0,sizeof(f));
        f[0]=1;A[0]=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%lld",&A[i]);
            A[i]%=mod;
        }
        solve(0,n);
        printf("%lld\n",f[n]);
    }

    return 0;
}
posted @ 2018-04-08 13:35  Maxwei_wzj  阅读(83)  评论(0编辑  收藏  举报