NKOJ1725 数字
时间限制 : 20000 MS 空间限制 : 128000 KB
问题描述
给出一个整数n和一个数组a。数组a中包含十个整数,编号0到9。
你的任务是统计满足下列条件的正整数的个数:
1.该正整数的长度不能超过n位;
2.该正整数的最高位不能是0;
3.数字i(0<=i<=9)在该正整数中至少出现a[i]次。
输入格式
第一行一个整数n(1<=n<=100)。
第二行,10个空格间隔的整数,表示a[0],a[1]…a[9]里面存的数字
输出格式
一行,一个整数,表示满足条件的数字的个数,结果可能很大,输出与1000000007取模后的结果。
样例输入
样例输入1:
1
0 0 0 0 0 0 0 0 0 1
样例输入2:
2
1 1 0 0 0 0 0 0 0 0
样例输入3:
3
1 1 0 0 0 0 0 0 0 0
样例输出
样例输入1:
1
样例输入2:
1
样例输入3:
36
提示
样例说明:
满足样例1的数字只有数字”9”
满足样例2的数字只有数字”10”
满足样例3的数字有”10,110,101,100,102,103,120,210,301,……”总共36个,这些数字都满足0出现了至少1次,1出现了至少1次,其他数字出现了至少0次,并且长度不超过3。
注:此题可能会用到排列组合的知识!
组合公式:Cnk=n!/((n-k)!*k!)=Cn-1k+Cn-1k-1
排列公式:Ank=n!/(n-k)!=k!*Cnk
Cn0=An0=1
来源 源自codeforce
思路:
状态:f[i][j]表示用 j~9的数字填i个位置的总方案数。
方程:
j==9时,f[i][j]=1(i>=a[9]) / f[i][j]=0(i< a[9])
j==1~8时,f[i][j]=sum{ c[i][k]*f[i-k][j+1]} (a[j]<=i<=n, a[j]<=k<=i)
//先考虑填j的情况,可以填c[i][k]种,然后再考虑用j+1~9填满i-k个格子的情况:f[i-k][j+1], 乘起来。
j==0时,f[i][j]=sum{ c[i-1][k]*f[i-k][j+1]} (a[j]<=i<=n, a[j]<=k<=i)
//大致同上,但0不能填最高位,所以填c[i-1][k]种
#include<cstdio>
#include<iostream>
using namespace std;
const long long mod=1000000007;
long long c[103][111],f[103][110];
long long a[10];
int main()
{
int n;scanf("%d",&n);
for(int i=0;i<=9;i++) scanf("%I64d",a+i);
for(int i=0;i<=n;i++)c[i][0]=1;
for(int i=1,j;i<=n;i++)
for(j=1;j<=i;j++)
c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
for(int i=a[9];i<=n;i++) f[i][9]=1;
for(int j=8,k,i;j>=0;j--)
{
for(i=a[j];i<=n;i++)
{
if(j!=0) for(k=a[j];k<=i;k++) f[i][j]=(f[i][j]+(c[i][k]*f[i-k][j+1])%mod)%mod;
else for(k=a[j];k<i;k++) f[i][0]=(f[i][0]+(c[i-1][k]*f[i-k][1])%mod)%mod;
}
}
long long ans=0;
for(int i=a[0];i<=n;i++)
{
ans=(ans+f[i][0])%mod;
}
printf("%I64d",ans);
}