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

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

#include 
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;iif (ifor(int mid=1;mid1)
    {
        Complex W={cos(pi/mid),type*sin(pi/mid)};
        for(int l=0;l1))
        {
            Complex w={1.0,0.0};
            for(int k=0;kif (type==-1)
    {
        for(int i=0;idouble)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;i0.0;
    for(int i=0;i1;i++)
        a[i].x=f[l+i];
    for(int i=0;i1];
    rev[0]=0;
    for(int i=1;i>1]>>1)|((i&1)<<(bit-1));
    FFT(a,1,x),FFT(b,1,x);
    for(int i=0;i1,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;
}

你可能感兴趣的:(分治-CDQ分治,数学-FFT/NTT/FWT)