让我们计算有多少种构造方式 ,使得数字串(不能有前导0)长度小于等于n,并且0~9的每个数字至少使用了a【i】个。
思路:
我们设定Dp【i】【j】表示长度为i,从数字9开始倒着插入数字,现在插到数字j的构造方案数。
那么有:
Dp【i】【j】=ΣDp【i-k】【j+1】*C(j,k);
注意一下j==0的情况,特判一下不能有前导0即可。
Ac代码:
#include<stdio.h>
#include<string.h>
using namespace std;
#define LL long long int
const LL mod = 1000000000+7;
const LL N = 300000+5;
const LL M = 3e5+3;
LL fac[1000005]; //阶乘
LL inv_of_fac[1000005]; //阶乘的逆元
LL qpow(LL x,LL n)
{
LL ret=1;
for(; n; n>>=1)
{
if(n&1) ret=ret*x%mod;
x=x*x%mod;
}
return ret;
}
void init()
{
fac[1]=1;
for(int i=2; i<=M; i++)
fac[i]=fac[i-1]*i%mod;
inv_of_fac[M]=qpow(fac[M],mod-2);
for(int i=M-1; i>=0; i--)
inv_of_fac[i]=inv_of_fac[i+1]*(i+1)%mod;
}
LL C(LL a,LL b)
{
if(b>a) return 0;
if(b==0) return 1;
return fac[a]*inv_of_fac[b]%mod*inv_of_fac[a-b]%mod;
}
int a[15];
LL dp[150][12];
int main()
{
init();
int n;
while(~scanf("%d",&n))
{
memset(dp,0,sizeof(dp));
for(int i=0;i<=9;i++)scanf("%d",&a[i]);
for(int i=a[9];i<=n;i++)dp[i][9]=1;
for(int i=8;i>=0;i--)
{
for(int j=0;j<=n;j++)
{
for(int k=a[i];k<=j;k++)
{
if(i!=0)dp[j][i]=(dp[j][i]+dp[j-k][i+1]*C(j,k))%mod;
else if(i==0&&j>k)
{
dp[j][i]=(dp[j][i]+dp[j-k][i+1]*C(j-1,k))%mod;
}
}
}
}
LL ans=0;
for(int i=0;i<=n;i++)ans=(ans+dp[i][0])%mod;
printf("%I64d\n",ans);
}
}