[联合集训6-11] 数数题 多项式+自然数幂和

8 篇文章 0 订阅
4 篇文章 0 订阅

因为保证了 Bi<Ai+1 B i < A i + 1 ,看起来就是一个进制数一样的东西。形象地理解问题就是可以把第 i i 位的1分配给第 i1 i − 1 位变成 Ai A i ,求一共有多少种分配方法。
那么我们设 ft(n) f t ( n ) 表示假设第 t t 位上是n i=1..t1 i = 1.. t − 1 位都是 Bi B i ),有多少种分配方案,要求的就是 fN(BN) f N ( B N ) 。通过枚举分给下一位多少,就有

ft(n)=i=0nft1(iAt+Bt1) f t ( n ) = ∑ i = 0 n f t − 1 ( i A t + B t − 1 )

因为 f1(n)=1 f 1 ( n ) = 1 ,那么 ft(n) f t ( n ) 就是一个关于 n n t1次多项式。我们考虑已知 ft1 f t − 1 来推 ft f t
ft1(n)=k=0t2aknkft(n)=k=0t2aki=0n(iAt+Bt1)k f t − 1 ( n ) = ∑ k = 0 t − 2 a k n k → f t ( n ) = ∑ k = 0 t − 2 a k ∑ i = 0 n ( i A t + B t − 1 ) k

通过二项式定理把 k k 次方展开,然后把k相同的项合并到一起去(可以发现对于相同的 k k ik的系数都相同),并求出这些系数 ck c k ,就是 ft(n)=t2k=0ckni=0ik f t ( n ) = ∑ k = 0 t − 2 c k ∑ i = 0 n i k 。通过斯特林数预处理出自然数幂和的系数,直接乘出来即可。
复杂度 O(n3) O ( n 3 )

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 35
#define ll long long
#define up(x,y) (x=(x+(y))%mod)
#define inv(x) ksm(x,mod-2)
using namespace std;
const int mod=323232323;
int n;
ll A[N],B[N],C[N][N],S[N][N];
ll ksm(ll a,ll b)
{
    ll r=1;
    for(;b;b>>=1,a=a*a%mod)
        if(b&1) r=r*a%mod;
    return r;
}
struct ploy
{
    int deg;
    ll a[N];
    ploy(){deg=0;memset(a,0,sizeof(a));}
    ploy operator *(ploy b)
    {
        ploy re;re.deg=deg+b.deg;
        for(int i=0;i<=deg;i++)
            for(int j=0;j<=b.deg;j++)
                up(re.a[i+j],a[i]*b.a[j]);
        return re;      
    } 
    ploy operator +(ploy b)
    {
        ploy re;re.deg=max(deg,b.deg);
        for(int i=0;i<=re.deg;i++)
            up(re.a[i],a[i]+b.a[i]);
        return re;  
    }
    ploy operator *(ll d)
    {
        ploy re;re.deg=deg;
        for(int i=0;i<=re.deg;i++)
            re.a[i]=a[i]*d%mod;
        return re;  
    }
    ll qry(ll x)
    {
        ll re=0;
        for(int i=0;i<=deg;i++)
            up(re,a[i]*ksm(x,i));
        return re;  
    }
}dpow[N],spow[N],f[N];

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%lld",&A[i]);
    for(int i=1;i<=n;i++)
        scanf("%lld",&B[i]);
    C[0][0]=1;
    for(int i=1;i<=n;C[i][0]=1,i++)
        for(int j=1;j<=i;j++)   
            C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
    S[0][0]=1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=i;j++)
            S[i][j]=(S[i-1][j-1]+S[i-1][j]*j)%mod;  
    dpow[0].a[0]=1;
    ploy now;
    now.deg=1;
    now.a[0]=mod+1;now.a[1]=1;
    for(int i=1;i<=n;i++)
        dpow[i]=dpow[i-1]*now,now.a[0]--;
    for(int k=0;k<n;k++)
        for(int j=0;j<=k;j++)
            spow[k]=spow[k]+dpow[j+1]*(S[k][j]*inv(j+1)%mod);

    f[1].a[0]=1;
    for(int t=2;t<=n;t++)
    {
        ploy tmp;f[t].deg=t-1;  
        for(int k=0;k<t-1;k++)      
            for(int j=0;j<=k;j++)
                up(tmp.a[j],C[k][j]*ksm(A[t],j)%mod*ksm(B[t-1],k-j)%mod*f[t-1].a[k]);
        for(int k=0;k<t-1;k++)
            f[t]=f[t]+spow[k]*tmp.a[k];     
    }
    printf("%lld",f[n].qry(B[n]));
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值