hdu5730 Shell('s) Necklace

题意

n n n种链子,第 i i i种链子长度为 i i i,有 a i a_i ai条,求用这些链子组合出长度为n的链子的方案数。

题解

开始居然没看出来这是一道dp题
f i f_i fi为凑出长度为 i i i的链子的方案数
则有;
f i = ∑ j = 0 i − 1 f j ∗ a i − j , f 0 = 1 f_i=\sum_{j=0}^{i-1}f_j*a_{i-j}, f_0=1 fi=j=0i1fjaij,f0=1
时间复杂度为 O ( n 2 ) O(n^2) O(n2)显然要T。
考虑优化。发现右半部分与卷积( a ∗ b = ∑ i = 1 n a i ∗ b i − j a*b=\sum_{i=1}^{n}a_i*b_{i-j} ab=i=1naibij)类似。所以考虑使用FFT
然而如果说每次转移暴力FFT,则时间复杂度瞬间飙至 O ( n 2 l o g 2 n ) O(n^2log_2n) O(n2log2n),不如不优化
于是我们介绍一个新科技:CDQ分治。
下面介绍一下它的基本步骤:

c d q ( l , r ) cdq(l, r) cdq(l,r)
1、求解 c d q ( l , m i d ) , m i d = ⌊ l + r 2 ⌋ cdq(l, mid), mid = \lfloor\frac{l+r}{2}\rfloor cdq(l,mid),mid=2l+r
2、计算 [ l , m i d ] [l,mid] [l,mid] [ m i d + 1 , r ] [mid+1,r] [mid+1,r]的贡献(对 [ m i d + 1 , r ] [mid+1, r] [mid+1,r]答案的影响)
3、求解 c d q ( m i d + 1 , r ) cdq(mid+1, r) cdq(mid+1,r)

继续yy优化。我们用 c d q ( l , r ) cdq(l,r) cdq(l,r)表示求解 f l , f l + 1 , f l + 2 . . . f r f_l, f_{l+1},f_{l+2}...f_r fl,fl+1,fl+2...fr
假设求出 c d q ( l , m i d ) cdq(l,mid) cdq(l,mid),令 g i g_i gi [ l , m i d ] [l, mid] [l,mid]对f[i]的贡献, h i , l = ∑ j = 0 l − 1 f j ∗ a i − j h_{i, l}=\sum_{j=0}^{l-1}f_j*a_{i-j} hi,l=j=0l1fjaij
由定义:
f i = ∑ j = 0 i − 1 f j ∗ a i − j = ∑ j = 0 l − 1 f j ∗ a i − j + ∑ j = m i d + 1 i − 1 f j ∗ a i − j + g i = h i , l + ∑ j = m i d + 1 i − 1 f j ∗ a i − j + ∑ j = l m i d f j ∗ a i − j \begin{aligned} f_i&=\sum_{j=0}^{i-1}f_j*a_{i-j}\\ &=\sum_{j=0}^{l-1}f_j*a_{i-j}+\sum_{j=mid+1}^{i-1}f_j*a_{i-j}+g_i\\ &=h_{i, l}+\sum_{j=mid+1}^{i-1}f_j*a_{i-j}+\sum_{j=l}^{mid}f_j*a_{i-j}\\ \end{aligned} fi=j=0i1fjaij=j=0l1fjaij+j=mid+1i1fjaij+gi=hi,l+j=mid+1i1fjaij+j=lmidfjaij
c d q ( l , r ) cdq(l,r) cdq(l,r)的过程可知, h i , l h_{i,l} hi,l已知, ∑ j = m i d + 1 i − 1 f j ∗ a i − j \sum_{j=mid+1}^{i-1}f_j*a_{i-j} j=mid+1i1fjaij将会在 c d q ( m i d + 1 , r ) cdq(mid+1,r) cdq(mid+1,r)中计算出,所以只需求 { f l , f l + 1 , . . . , f m i d } ∗ { a 1 , a 2 , . . . , a r − l + 1 } \{f_l,f_{l+1},...,f_{mid}\}*\{a_1,a_2,...,a_{r-l+1}\} {fl,fl+1,...,fmid}{a1,a2,...,arl+1}即可。
c d q ( l , r ) cdq(l,r) cdq(l,r)的过程时间复杂度为 O ( l o g 2 n ) O(log_2n) O(log2n),FFT的时间复杂度为 O ( n l o g 2 n ) O(nlog_2n) O(nlog2n),故总时间复杂度为 O ( n l o g 2 2 n ) O(nlog_2^2n) O(nlog22n)

代码

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int mn = 100005, mod = 313;
const double pi = acos(-1.0);
struct com{
    double r, i;
    com(double a = 0, double b = 0) : r(a), i(b) {}
    com operator +(const com& t) {return com(r + t.r, i + t.i);}
    com operator -(const com& t) {return com(r - t.r, i - t.i);}
    com operator *(const com& t) {return com(r * t.r - i * t.i, r * t.i + i * t.r);}
}x[mn << 1], y[mn << 1], w, wn;
int a[mn], f[mn];
void change(com *x, int n)
{
    for(int i = 1, j = n >> 1; i < n - 1; i++)
    {
        if(i < j)
            swap(x[i], x[j]);
        int k = n >> 1;
        while(j >= k)
            j -= k, k >>= 1;
        j += k;
    }
}
void fft(com x[], int n, int flag)
{
    change(x, n);
    for(int i = 2; i <= n; i <<= 1)
    {
        wn = com(cos(flag * 2 * pi / i), sin(flag * 2 * pi / i));
        for(int j = 0; j < n; j += i)
        {
            w = com(1, 0);
            for(int k = j; k < j + i / 2; k++)
            {
                com u = w * x[k + i / 2], v = x[k];
                x[k] = v + u, x[k + i / 2] = v - u, w = w * wn;
            }
        }
    }
    if(flag == -1)
        for(int i = 0; i < n; i++)
            x[i].r /= n;
}
void cdq(int l, int r)
{
    if(l == r)
    {
        f[l] += a[l], f[l] %= mod;
        return;
    }
    int mid = (l + r) >> 1, i;
    cdq(l, mid);
    int len = 1;
    while(len <= r - l + 1)
        len <<= 1;
    for(i = 0; i < len; i++)
        x[i] = y[i] = com(0, 0);
    for(i = l; i <= mid; i++)
        x[i - l] = com(f[i], 0);
    for(i = 1; i <= r - l + 1; i++)
        y[i - 1] = com(a[i], 0);
    fft(x, len, 1), fft(y, len, 1);
    for(i = 0; i < len; i++)
        x[i] = x[i] * y[i];
    fft(x, len, -1);
    for(i = mid + 1; i <= r; i++)
        f[i] += int(x[i - l - 1].r + 0.5), f[i] %= mod;
    cdq(mid + 1, r);
}
int main()
{
    int n, i;
    while(~scanf("%d", &n) && n)
    {
        for(i = 1; i <= n; i++)
            scanf("%d", &a[i]), a[i] %= mod, f[i] = 0;
        cdq(0, n);
        printf("%d\n", f[n]);
    }
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值