可(数位dp)

题目

g ( n ) g(n) g(n) ∑ i = 1 k a i = n \sum\limits_{i=1}^ka_i=n i=1kai=n 的方案数,答案即为 ∑ i = 0 k x f ( i ) g ( i ) \sum\limits_{i=0}^{kx}f(i)g(i) i=0kxf(i)g(i)

考虑容斥去求 g ( n ) g(n) g(n)
g ( n ) = ∑ i = 0 k ( − 1 ) i ( k i ) ( n − i ( x + 1 ) + k − 1 k − 1 ) g(n)=\sum\limits_{i=0}^k(-1)^i\binom{k}{i}\binom{n-i(x+1)+k-1}{k-1} g(n)=i=0k(1)i(ik)(k1ni(x+1)+k1)

简单来说就是枚举有 ≥ i \ge i i 个数是大于 x x x 的,在 k k k 个数中选,然后把这 k k k 个数都减去 x + 1 x+1 x+1(减完后剩余的部分任意选),用插板法求出方案数( ∑ i = 1 n x i = m \sum\limits_{i=1}^nx_i=m i=1nxi=m 非负整数解的数量是 ( n + m − 1 n − 1 ) \binom{n+m-1}{n-1} (n1n+m1))。

代入答案式子
∑ i = 0 k x f ( i ) g ( i ) = ∑ i = 0 k x f ( i ) ∑ j = 0 k ( − 1 ) j ( k i ) ( n − j ( x + 1 ) + k − 1 k − 1 ) = ∑ j = 0 k ( − 1 ) j ( k i ) ∑ i = 0 k x f ( i ) ( n − j ( x + 1 ) + k − 1 k − 1 ) = 1 ( k − 1 ) ! ∑ j = 0 k ( − 1 ) j ( k i ) ∑ l = 0 k − 1 h j , l ∑ i = 0 k x f ( i ) i l \begin{aligned} \sum\limits_{i=0}^{kx}f(i)g(i)&=\sum\limits_{i=0}^{kx}f(i)\sum\limits_{j=0}^k(-1)^j\binom{k}{i}\binom{n-j(x+1)+k-1}{k-1}\\ &=\sum\limits_{j=0}^k(-1)^j\binom{k}{i}\sum\limits_{i=0}^{kx}f(i)\binom{n-j(x+1)+k-1}{k-1}\\ &=\dfrac{1}{(k-1)!}\sum\limits_{j=0}^k(-1)^j\binom{k}{i}\sum\limits_{l=0}^{k-1}h_{j,l}\sum\limits_{i=0}^{kx}f(i)i^l\\ \end{aligned} i=0kxf(i)g(i)=i=0kxf(i)j=0k(1)j(ik)(k1nj(x+1)+k1)=j=0k(1)j(ik)i=0kxf(i)(k1nj(x+1)+k1)=(k1)!1j=0k(1)j(ik)l=0k1hj,li=0kxf(i)il

其中 h j , l = [ i l ] ( i − ( x + 1 ) j + k − 1 ) k − 1 h_{j,l}=[i^l](i-(x+1)j+k-1)^{k-1\over} hj,l=[il](i(x+1)j+k1)k1。把 i i i 看作多项式中的变量,那么 h j , l h_{j,l} hj,l 就是 l l l 次的系数。

j j j 是常量,我们可以用 D P DP DP O ( k 2 ) O(k^2) O(k2) 的时间求出所有 h j , l h_{j,l} hj,l。就考虑前 i i i 个数,选了 j j j 个的全部方案的和。

这里有个问题,如果 i < T i<T i<T,组合数拆成下降幂就会有问题。所以 i i i 有下界 T = j ( x + 1 ) T=j(x+1) T=j(x+1)

问题转换为求 ∑ i = T k x f ( i ) i l \sum\limits_{i=T}^{kx}f(i)i^l i=Tkxf(i)il

在这里,为了让求值更方便,考虑 i > k x i>kx i>kx g ( i ) g(i) g(i) 是多少,显然由定义和容斥得 g ( i ) = 0 g(i)=0 g(i)=0。所以上界可以忽略,为了下面数位 D P DP DP 方便,将上界设为 999999 … 999999\dots 999999(大于等于 k x kx kx)。

先考虑没有下界的情况。

d p j = ∑ f ( i ) i j dp_{j}=\sum f(i)i^j dpj=f(i)ij,从高位往低位 D P DP DP n o w now now 是当前位, a a a 是当前位填什么数字。

d p j = ∑ a = 0 9 ∑ f ( i ) f ( a ) ( i + a × 1 0 n o w ) j = ∑ a = 0 9 ∑ f ( i ) f ( a ) ∑ l = 0 j ( j l ) i j − l ( a × 1 0 n o w ) l = ∑ a = 0 9 ∑ l = 0 j ( j l ) f ( a ) ( a × 1 0 n o w ) j − l d p l \begin{aligned} dp_{j}&=\sum\limits_{a=0}^9\sum f(i)f(a)(i+a\times10^{now})^j\\ &=\sum\limits_{a=0}^9\sum f(i)f(a)\sum\limits_{l=0}^j\binom{j}{l}i^{j-l}(a\times10^{now})^l\\ &=\sum\limits_{a=0}^9\sum\limits_{l=0}^j\binom{j}{l}f(a)(a\times10^{now})^{j-l}dp_{l} \end{aligned} dpj=a=09f(i)f(a)(i+a×10now)j=a=09f(i)f(a)l=0j(lj)ijl(a×10now)l=a=09l=0j(lj)f(a)(a×10now)jldpl

时间复杂度 O ( 10 k 2 lg ⁡ x ) O(10k^2\lg x) O(10k2lgx)

对于有下界的情况,设 f o , l = ∑ i = T f ( i ) i l f_{o,l}=\sum\limits_{i=T}f(i)i^l fo,l=i=Tf(i)il(前 o o o 位), g o , l = ∑ i > T f ( i ) i l g_{o,l}=\sum\limits_{i>T}f(i)i^l go,l=i>Tf(i)il(前 o o o 位),同样从高位往低位转移。

T T T 当前位的数字为 A A A c i , j = f ( i ) ( i × 1 0 n o w ) j c_{i,j}=f(i)(i\times10^{now})^j ci,j=f(i)(i×10now)j d i , j = ∑ l = i k − 1 c l , j d_{i,j}=\sum\limits_{l=i}^{k-1}c_{l,j} di,j=l=ik1cl,j(后缀和)。

先给出转移
f i , j = ∑ l = 0 j ( j l ) c A , j − l f i + 1 , l f_{i,j}=\sum\limits_{l=0}^j\binom{j}{l}c_{A,j-l}f_{i+1,l} fi,j=l=0j(lj)cA,jlfi+1,l
g i , j = ∑ l = 0 j ( j l ) d 0 , j − l ⋅ g i + 1 , l + ∑ l = 0 j ( j l ) d A + 1 , j − l ⋅ f i + 1 , l g_{i,j}=\sum\limits_{l=0}^j\binom{j}{l}d_{0,j-l}\cdot g_{i+1,l}+\sum\limits_{l=0}^j\binom{j}{l}d_{A+1,j-l}\cdot f_{i+1,l} gi,j=l=0j(lj)d0,jlgi+1,l+l=0j(lj)dA+1,jlfi+1,l

f i , j f_{i,j} fi,j 的转移和上面 d p dp dp 是一样的,只不过不用枚举当前位填什么数字,就填 A A A

g i , j g_{i,j} gi,j 由于 i i i 有下界 T T T,若前面的位已经比 T T T 大,后面无论填什么数字都比 T T T 大,所以加上 ( j l ) d 0 , j − l ⋅ g i + 1 , l \binom{j}{l}d_{0,j-l}\cdot g_{i+1,l} (lj)d0,jlgi+1,l;如果前面的位和 T T T 的一样,那么这一位填比 A A A 大的数,也是比 T T T 大的,所以加上 ( j l ) d A + 1 , j − l ⋅ f i + 1 , l \binom{j}{l}d_{A+1,j-l}\cdot f_{i+1,l} (lj)dA+1,jlfi+1,l

至此,我们已经解决这个问题,总的时间复杂度为 O ( k 3 lg ⁡ n ) O(k^3\lg n) O(k3lgn)

具体实现参见代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int N=1e4+10,K=25;
int n,k;
ll f[N][K],g[N][K],C[K][K],ans,b[N],pw[N],a[N],h[K],c[K][K],d[K][K];
char s[N];
ll ksm(ll a,ll b)
{
    ll ans=1;
    while(b){
        if(b&1) ans=ans*a%mod;
        b>>=1;
        a=a*a%mod;
    }
    return ans;
}
void init()
{
    C[0][0]=1;
    for(int i=1;i<=k;i++){
        C[i][0]=1;
        for(int j=1;j<=i;j++){
            C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
        }
    }
    for(int i=0;i<n;i++) b[i]=s[n-i-1]^48;
    n+=3;
    b[0]++;
    for(int i=0;i<=n;i++) b[i+1]+=b[i]/10,b[i]%=10;
    pw[0]=1;
    for(int i=1;i<=n;i++) pw[i]=pw[i-1]*10%mod;
}
ll solve()
{
    memset(f,0,sizeof(f));
    memset(g,0,sizeof(g));
    f[n+1][0]=1;
    for(int K=n,A;K>=0;K--){
        A=a[K];
        for(int i=9,X,Y;i>=0;i--){
            X=max(i,1),Y=i*pw[K]%mod;
            c[i][0]=d[i][0]=X;
            d[i][0]=(d[i][0]+d[i+1][0])%mod;
            for(int j=1;j<k;j++){
                c[i][j]=d[i][j]=c[i][j-1]*Y%mod;
                d[i][j]=(d[i][j]+d[i+1][j])%mod;
            }
        }
        for(int i=k-1;i>=0;i--){
            for(int j=0;j<=i;j++){
                (g[K][i]+=g[K+1][j]*C[i][j]%mod*d[0][i-j])%=mod;
                (g[K][i]+=f[K+1][j]*C[i][j]%mod*d[A+1][i-j])%=mod;
            }
            for(int j=0;j<=i;j++){
                (f[K][i]+=f[K+1][j]*C[i][j]%mod*c[A][i-j])%=mod;
            }
        }
    }
    ll num=0;
    for(int i=n;i>=0;i--) num=(num+pw[i]*a[i])%mod;
    memset(h,0,sizeof(h));
    h[0]=1;
    for(int i=1;i<k;i++){
        for(int j=i;j>=1;j--){
            h[j]=(h[j]+h[j-1]*(i-num+mod))%mod;
        }
    }
    ll ans=0;
    for(int i=0;i<k;i++) ans=(ans+h[k-1-i]*(f[0][i]+g[0][i]))%mod;
    return ans;
}
int main()
{
    scanf("%d%s",&k,s);
    n=strlen(s);
    init();
    for(int i=0;i<=k;i++){
        ans=(ans+(i&1?-1:1)*C[k][i]*solve())%mod;
        for(int j=0;j<=n;j++){
            a[j]+=b[j];
            a[j+1]+=a[j]/10,a[j]%=10;
        }
    }
    for(int i=1;i<k;i++) ans=ans*ksm(i,mod-2)%mod;
    cout<<(ans+mod)%mod;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值