HDU 5730 Shell Necklace(FFT+分治)

[题目链接]

[题意]
有一串长度为 n 的贝壳项链,装饰连续的i颗贝壳有 ai 种方案。在每颗贝壳都仅被装饰一次的情况下,有多少种装饰方案?

[分析]
设装饰前 i 颗贝壳的方案数为f(i) , 则容易得到递推式 f(i)=ij=1f(ij)aj
朴素的计算复杂度为O(n^2),肯定T

观察递推式的形式,考虑用FFT+分治进行优化

计算[L,R]内的 f 时:
先计算[L,M]的f
利用已经计算好的[L,M]的 f 值去更新[M+1,R]内的f,这个过程可以利用FFT加速卷积
计算[M+1,R]的 f

我们以样例2为例详细说明分治具体过程
n=4 
a1=a2=a3=a4=2

计算(1,4)

计算(1,2)

计算(1,1)
f1 += a1 => f1=2 

用FFT计算卷积 {a1}{f1}={2}{2}={4} 
f2 += 4 => f2=4 

计算(2,2)
f2 += a2 => f2=6 

用FFT计算卷积 {a1,a2,a3}{f1,f2}={2,2,2}{2,6}={4,16,16,12} 
f3 += 16=>f3=16
f4 += 16=>f4=16

计算(3,4)

计算(3,3)
f3 += a3 => f3=18 

用FFT计算卷积 {a1}{f3}={2}{18}={36} 
f4 += 36 => f4=52 

计算(4,4)
f4 += a4 => f4=54 

[代码]

#include <bits/stdc++.h>
using namespace std ;
const int N = 1e5 + 5 ;
const int mod = 313 ;
const double pi = acos(-1.0) ;
typedef long long LL ;

int n , m ;
int a[N] , f[N] ;

struct Complex
{
    double x , y ;
    Complex( double x = 0 , double y = 0 ):x(x),y(y) { }
} b[N<<1] , c[N<<1] ;

Complex operator + ( Complex A , Complex B )
{
    return Complex(A.x+B.x,A.y+B.y) ;
}

Complex operator - ( Complex A , Complex B )
{
    return Complex(A.x-B.x,A.y-B.y) ;
}

Complex operator * ( Complex A , Complex B )
{
    return Complex(A.x*B.x-A.y*B.y,A.x*B.y+A.y*B.x) ;
}

Complex operator / ( Complex A , int b )
{
    return Complex(A.x/b,A.y/b) ;
}

void change( Complex y[] , int len )
{
    for( int i = 1 , j = len>>1 ; i+1 < len ; i++ )
    {
        if( i < j ) swap(y[i],y[j]) ;
        int k = len>>1 ;
        while( j >= k )
        {
            j -= k ;
            k >>= 1 ;
        }
        if( j < k ) j += k ;
    }
}

void FFT( Complex y[] , int len , int on )
{
    change(y,len) ;
    for( int i = 2 ; i <= len ; i <<= 1 )
    {
        Complex wn(cos(-on*2*pi/i),sin(-on*2*pi/i)) ;
        for( int j = 0 ; j < len ; j += i )
        {
            Complex w(1) ; int o = i>>1 ;
            for( int k = j ; k < j+o ; k++ )
            {
                Complex u = y[k] , t = w * y[k+o] ;
                y[k] = u+t ;
                y[k+o] = u-t ;
                w = w * wn ;
            }
        }
    }
    if( on == -1 )
    {
        for( int i = 0 ; i < len ; i++ )
            y[i] = y[i] / len ;
    }
}

void cal( int L , int R )
{
    if( L == R )
    {
        f[L] = (f[L]+a[L]) % mod ;
        return ;
    }
    int M = (L+R)>>1 ;
    cal(L,M) ;
    int len = 1 , m = R-L+1 ;
    while( len <= m ) len <<= 1 ;
    for( int i = 0 ; i < len ; i++ )
    {
        if( L+i <= M ) b[i] = Complex(f[L+i]) ;
        else b[i] = Complex() ;
        if( L+i+1 <= R ) c[i] = Complex(a[i+1]) ;
        else c[i] = Complex() ;
    }
    FFT(b,len,1) ;
    FFT(c,len,1) ;
    for( int i = 0 ; i < len ; i++ )
        b[i] = b[i] * c[i] ;
    FFT(b,len,-1) ;
    for( int i = M+1 ; i <= R ; i++ )
        f[i] = ( f[i] + (int)(b[i-L-1].x+0.5) ) % mod ;
    cal(M+1,R) ;
}

int main()
{
    while( scanf( "%d" , &n ) , n )
    {
        for( int i = 1 ; i <= n ; i++ )
        {
            scanf( "%d" , &a[i] ) ;
            a[i] %= mod ;
        }
        memset(f,0,sizeof(f)) ;
        cal(1,n) ;
        printf( "%d\n" , f[n] ) ;
    }
    return 0 ;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值