Shell Necklace HDU - 5730 [FFT+CDQ分治]

Shell Necklace HDU - 5730 [FFT+CDQ分治]

Tags: FFT CDQ分治


Shell Necklace HDU - 5730

题意:

S为n的一种分割的形式,表示为一个正整数序列,令S[i]表示序列的第i个元素,那么满足下式

i=1|S|S[i]=n ∑ i = 1 | S | S [ i ] = n

要求的是
res=Si=1|S|S[i] r e s = ∑ S ∏ i = 1 | S | S [ i ]

分析:

但是这个样子并不好?考虑计算贡献?
但是如果这样计算贡献的话就需要乘上之前的东西。
记dp[i]为 已经填了i个的方案总数。
dp[0]=1;

dp[i]=j=0i1dp[j]a[ij] d p [ i ] = ∑ j = 0 i − 1 d p [ j ] ∗ a [ i − j ]

看起来很像能随便优化的样子。不过每次计算都用到了之前的dp值。所以其实并不能直接用FFT来。
需要求出的只有dp[n]

但是感觉起来就是经典模型啊qwq
难道是快速幂fft?
[不行这样就连续两个快速了]

如果知道了 dp[0,l/2] d p [ 0 , l / 2 ] 的话能不能快速求出dp[0,l]呢。
考虑添加贡献qwq

dp[i]=j=lmiddp[j]a[ij] d p [ i ] = ∑ j = l m i d d p [ j ] ∗ a [ i − j ]

稍微转化一下
dp[j+k]=j=lmiddp[j]a[k] d p [ j + k ] = ∑ j = l m i d d p [ j ] ∗ a [ k ]

//2018年06月15日 星期五 21时25分54秒

简单来说就是用CDQ分治来实现dp从小到大逐个更新的效果。

code
//2018年06月15日 星期五 21时25分54秒 
#include<bits/stdc++.h>
#define M 200005
#define ll long long
#define mo 313
#define db long double
using namespace std;
void read(int &x){
    x=0; char c=getchar();
    for (;c<48;c=getchar());
    for (;c>47;c=getchar())x=(x<<1)+(x<<3)+(c^48);
}
struct Complex{
    db r,i;
    Complex(db R=0,db I=0){
        r=R; i=I;
    }
};
Complex operator +(const Complex &x,const Complex &y){
    return Complex(x.r+y.r,x.i+y.i);
}
Complex operator -(const Complex &x,const Complex &y){
    return Complex(x.r-y.r,x.i-y.i);
}
Complex operator *(const Complex &x,const Complex &y){
    return Complex(x.r*y.r-x.i*y.i,x.r*y.i+x.i*y.r);
}
Complex operator /(const Complex &x,const int &y){
    return Complex(x.r/y,x.i/y);
}
const db pi=acos(-1.0);
int rev[M<<2];
struct FFT{
    Complex a[M<<2];
    Complex &operator[](int i){
        return a[i];
    }
    void clear(int n){
        int i;
        for (i=0;i<n;i++)a[i]=Complex(0,0);
    }
    void solve(int n,int DFT){
        register int i,j,k,m;
        Complex w,wn,l,r;
        for (i=0;i<n;i++)if (rev[i]<i)swap(a[rev[i]],a[i]);
        for (m=1;m<n;m<<=1){
            k=m<<1;
            w=Complex(1,0);
            wn=Complex(cos(pi*DFT/m),sin(pi*DFT/m));
            for (i=0;i<m;i++){
                for (j=i;j<n;j+=k){
                    l=a[j];
                    r=a[j+m];
                    a[j]=l+r*w;
                    a[j+m]=l-r*w;
                }
                w=w*wn; 
            }
        }
        if (DFT==-1){
            for (i=0;i<n;i++)a[i]=a[i]/n;
        }
    }
}A,B;
int k,dp[M],a[M];

void solve(int l,int r){
    if (l==r){
        return ;
    }
    int mid=(l+r)>>1,i;
    solve(l,mid);
    int len=r-l+1;
    for (k=1;k<=len;k<<=1);
    for (i=0;i<k;i++)rev[i]=(rev[i>>1]>>1)|((i&1)*(k>>1));
    A.clear(k); B.clear(k);
    for (i=l;i<=mid;i++){
        A[i-l]=Complex(dp[i],0);
    }
    for (i=1;i<len;i++){
        B[i]=Complex(a[i],0);
    }
    A.solve(k,1); B.solve(k,1);
    for (i=0;i<k;i++)A[i]=A[i]*B[i];
    A.solve(k,-1);
    for (i=mid+1;i<=r;i++){
        dp[i]=(dp[i]+(ll)(A[i-l].r+0.5))%mo;
    }
    solve(mid+1,r);
}
int main(){
//  freopen("1.in","r",stdin);
    int n,i;
    for (read(n);n;read(n)){
        for (i=1;i<=n;i++)read(dp[i]),a[i]=dp[i];
        solve(1,n);
        printf("%d\n",(dp[n]%mo+mo)%mo);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值