动态规划学习系列——数位DP(练手四)

来源:互联网 发布:专家网络咨询公司排名 编辑:程序博客网 时间:2024/04/29 07:16

题目链接:hihoCoder 1033

解题思路:
思路超级简单,就是单纯的数位DP,但是,还是觉得好恶心。需要特殊考虑的就是第一位数字为0的情况,不能直接把状态转移的结果拿出来。
状态:dp[i][j][k+100],i 位数中以 j 开头的数交叉和为k的数量
(这里因为k有可能取负数,所以需要让它变正,加100即可)

代码:

#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>#define MOD 1000000007#define mid 100using namespace std;long long L,R,M,res[20][10][205],dp[20][10][205];long long base[20];int cnt,tmp[20];void cal(long long num){    cnt=0;    while(num)    {        tmp[++cnt]=num%10;        num/=10;    }}long long solve(long long num){    if(num==0)        return 0;    memset(dp,0,sizeof(dp));    memset(res,0,sizeof(res));    cal(num);    for(int i=0;i<10;i++)        res[1][i][i+mid]=i, dp[1][i][i+mid]=1;    for(int i=2;i<=cnt;i++)    {        for(int j=0;j<10;j++)        {            for(int k=0;k<10;k++)            {                for(int l=0;l<200;l++)                {                    dp[i][j][j-l+2*mid] = (dp[i][j][j-l+2*mid] + dp[i-1][k][l])%MOD;                    res[i][j][j-l+2*mid] = (res[i][j][j-l+2*mid] + res[i-1][k][l])%MOD;                    res[i][j][j-l+2*mid] = (res[i][j][j-l+2*mid] + ((j*base[i])%MOD)*dp[i-1][k][l])%MOD;                }            }        }    }    long long ans=0,val=0,sum=0;    for(int i=1;i<cnt;i++)    {        for(int j=1;j<10;j++)        {            ans = (ans+res[i][j][M+mid])%MOD;        }    }    for(int i=cnt;i>=1;i--)    {        for(int j=0;j<tmp[i];j++)        {            if(i==cnt&&j==0)                continue;            if(i%2==cnt%2)                ans = (ans+(sum*dp[i][j][M-val+mid])%MOD+res[i][j][M-val+mid])%MOD;            else                ans = (ans+(sum*dp[i][j][val-M+mid])%MOD+res[i][j][val-M+mid])%MOD;        }        sum = ((base[i]*tmp[i])%MOD + sum)%MOD;        if(i%2==cnt%2)            val+=tmp[i];        else            val-=tmp[i];    }    return ans;}int main(){    base[1]=1;    for(int i=2;i<=18;i++)        base[i]=(10*base[i-1])%MOD;    while(~scanf("%lld %lld %lld",&L,&R,&M))    {        printf("%lld\n",(solve(R+1) - solve(L) + MOD)%MOD);    }    return 0;}

总结:
记住负数取模要先加模数!!!
千万记住0这种特殊情况!!!

0 0
原创粉丝点击