[数位dp] hdu 4722 Good Numbers

来源:互联网 发布:淘宝店招在线生成 编辑:程序博客网 时间:2024/05/22 17:48

题意:问a到b之间有多少个位数和能被10整除的数

思路:

好像比别人的麻烦,不过是自己的风格~

dp[i][j][k] 代表有i位数,首位是j,在这之前位数和是k 的有多少个。

递推方程很好想 就比如要求

 比如要求dp[3][3][k] 其实就是 0~299+300~399 

就是 0~299+k-3:0~99 就是dp[i][j-1][k]+dp[i-1][10][(k+10-j+1)%10]

然后其实就是求sum(b)-sum(a-1)

比如求123的话

分解成 0:0~99,1:0~19,1+2:0~3

冒号前代表已经拥有的位数和,对应到dp数组里就是

dp[3][1][0]+dp[2][2][9]+dp[1][4][7]

注意考虑一下 -1和0的情况就好了。

代码:

#include"cstdlib"#include"cstdio"#include"cstring"#include"cmath"#include"queue"#include"algorithm"#include"iostream"#define eps 1e-8using namespace std;__int64 dp[22][12][12];__int64 slove(__int64 a){    if(a==-1) return 0;  //-1的时候    if(a<10)  return 1;  //一位数直接输出    __int64 ans=0;    __int64 len=(__int64)log10(a*1.0)+1;  //求位数    __int64 t=1,n=len-1;    while(n--) t*=10;  //构造那个位数对应的整十数    __int64 sum=0;     while(len)  //枚举每位    {        int tep=a/t;  //当前位是什么        if(len==1) tep++;  //这里注意如果是1位数 要+1        ans+=dp[len][tep][(10-sum)%10];        sum+=tep;  //已拥有位数和        sum%=10;   //记得取模        len--;        a%=t;        t/=10;    }    return ans;}int main(){    int i,j,k;    memset(dp,0,sizeof(dp));    for(k=0; k<10; k++) dp[1][10][k]=1;   //初始化  为了方便设个10    for(i=1; i<10; i++)    {        for(j=0; j<i; j++) dp[1][i][j]=1;    }    for(i=2; i<=19; i++)    {        for(j=1; j<=10; j++)        {            for(k=0; k<=9; k++)            {                if(j==1)                {                    dp[i][j][k]=dp[i-1][10][k];  //其实1就是上一层10的结果                    continue;                }                dp[i][j][k]+=dp[i][j-1][k];                dp[i][j][k]+=dp[i-1][10][(k+10-j+1)%10];  //递推方程            }        }    }    int t,cas=1;    cin>>t;    while(t--)    {        __int64 a,b;        scanf("%I64d%I64d",&a,&b);        printf("Case #%d: %I64d\n",cas++,slove(b)-slove(a-1));    }    return 0;}


0 0