【题】【数位动规】NKOJ1725 数字

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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值