HDU 5730 FFT + 分治

题目链接


题意:
给定一个数组 a a a,定义 a [ i ] a[i] a[i]表示连续 i i i个珍珠可以装饰的方案数,求n个珍珠的项链可以装饰的方案总数。


思路:
定义:
F [ i ] F[i] F[i] i i i个珍珠的项链可以装饰的方案总数
则很明显是一个动态规划,得:
F [ 0 ] = 1 F[0] = 1 F[0]=1
F [ i ] = ∑ j = 1 i a [ j ] ∗ F [ i − j ] F[i] = \sum_{j=1}^i a[j]*F[i-j] F[i]=j=1ia[j]F[ij]


可见维护的时间复杂度是 O ( n 2 ) O(n^2) O(n2)
可以利用分治+FFT,
每次合并区间时,计算左半区间对右半区间的贡献。
时间复杂度: O ( n log ⁡ 2 ( n ) ) O(n \log^2(n)) O(nlog2(n))


代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N = 524300, P = 313, M = 1000;
int n, i, j, k, pos[N], A[N], B[N], C[N], F[N], Q[N];
namespace FFT{
struct comp{
    long double r, i;comp(long double _r=0, long double _i=0){r=_r; i=_i;}
    comp operator + (const comp x){return comp(r+x.r, i+x.i);}
    comp operator - (const comp x){return comp(r-x.r, i-x.i);}
    comp operator * (const comp x){return comp(r*x.r-i*x.i,r*x.i+i*x.r);}
    comp conj(){return comp(r, -i);}
}A[N], B[N];
int a0[N], b0[N], a1[N], b1[N];
const long double pi = acos(-1.0);
void FFT(comp a[], int n, int t){
    for (int i = 1; i < n; i++) if (i < pos[i]) swap(a[i], a[pos[i]]);
    for (int d = 0; (1<<d) < n; d++) {
        int m = 1<<d, m2 = m<<1;
        long double o = pi*2/m2*t;comp _w(cos(o), sin(o));
        for (int i = 0; i < n; i += m2) {
            comp w(1,0);
            for (int j = 0; j < m; j++) {
                comp &A = a[i+j+m], &B = a[i+j], t = w*A;
                A = B - t; B = B + t; w = w * _w;
            }
        }
    }
    if (t == -1) for (int i=0; i < n; i++) a[i].r /= n;
}
void mul(int *a, int *b, int *c){
    int i, j;
    for (i = 0; i < k; i++) A[i] = comp(a[i], b[i]);
    FFT(A, k, 1);
    for (i = 0; i < k; i++) {
        j = (k-i)&(k-1);
        B[i] = (A[i]*A[i] -  (A[j]*A[j]).conj()) * comp(0, -0.25);
    }
    FFT(B, k, -1);
    for (i = 0; i < k; i++) c[i] = ((long long)(B[i].r + 0.5)) % P;
}
void mulmod(int *a, int *b, int *c){
    int i;
    for (i = 0; i < k; i++) a0[i] = a[i]/M, b0[i] = b[i]/M;
    for (mul(a0, b0, a0), i=0; i<k; i++) {
        c[i] = 1LL*a0[i]*M*M%P;
        a1[i] = a[i]%M, b1[i] = b[i] % M;
    }
    for (mul(a1, b1, a1), i=0; i<k; i++) {
        c[i] = (a1[i]+c[i])%P, a0[i]=(a0[i]+a1[i])%P;
        a1[i]=a[i]/M+a[i]%M, b1[i]=b[i]/M+b[i]%M;
    }
    for (mul(a1, b1, a1), i = 0; i < k; i++) c[i] = (1LL*M*(a1[i]-a0[i]+P)+c[i])%P;
}
}

void Init(int len){
    for (k = 1; k < len; k<<=1);
    j = __builtin_ctz(k) - 1;
    for (i = 0; i < k; i++) pos[i] = pos[i>>1]>>1|((i&1)<<j);
}

void CDQ(int l, int r){
    if (l >= r) return;
    int mid = (l+r)>>1;
    CDQ(l, mid);
    Init(r-l+1);
    for (int i = 0; i < k; i++) A[i] = B[i] = 0;
    for (int i = l; i <= mid; i++) A[i-l] = F[i];
    for (int i = 0; i <= r-l; i++) B[i] = Q[i];
    FFT::mulmod(A, B, C);
    for (int i = mid+1; i <= r; i++) F[i] = (F[i] + C[i-l]) % P;
    CDQ(mid + 1, r);
}

int main(){
    while (~scanf("%d", &n) && n) {
        for (i = 1; i <= n; i++) {
            int x;
            scanf("%d", &x);
            F[i] = Q[i] = x;
        }
        CDQ(0, n);
        printf("%d\n", (F[n]+P) % P);
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值