首页 > 代码库 > [数位dp] hdu 3967 Zero's Numberd

[数位dp] hdu 3967 Zero's Numberd

题意:对于两个数i和k,把它分为两个部分的数,n和m,如果(n+m)%k=0 那么这算一种分法

比如 333可分成,3、33或者33、3,对于 (333,3)就等于2.

现在给出 a、b、k,为 (a~b,k)有多少种分法

思路:对于一个数,注意前导零并枚举分点就好了。

 dp[22][22][22][22][2],   代表 i位,分点为fd,余数mod,对于k取余,是否有前导零


代码:

#include"cstdlib"
#include"cstdio"
#include"cstring"
#include"cmath"
#include"stack"
#include"algorithm"
#include"iostream"
using namespace std;
__int64 dp[22][22][22][22][2],ten[22];  //i位,分点为fd,余数mod,对于k取余,是否有前导零
int num[22],cnt;
__int64 dfs(int site,int fd,int mod,int k,int ff,int f)
{
    if(site==0)  return mod?0:1;
    if(!f&&dp[site][fd][mod][k][ff]!=-1) return dp[site][fd][mod][k][ff];
    int len=f?num[site]:9;
    __int64 ans=0;
    for(int i=0; i<=len; i++)
    {
        if(site>fd)
        {
            if(ff==0&&site==fd+1&&i==0) continue; //前导0不能到分点前,也就是前半部分不能为0
            ans+=dfs(site-1,fd,(i*ten[site-fd]+mod)%k,k,ff||i!=0,f&&i==len);
        }
        else
            ans+=dfs(site-1,fd,(i*ten[site]+mod)%k,k,ff||i!=0,f&&i==len);
    }
    if(!f) dp[site][fd][mod][k][ff]=ans;
    return ans;
}
__int64 solve(__int64 x,int k)
{
    cnt=0;
    if(x<10) return 0;
    __int64 ans=0;
    while(x)
    {
        num[++cnt]=x%10;
        x/=10;
    }
    for(int i=1; i<cnt; i++)  //枚举分点 分界为这位之后
        ans+=dfs(cnt,i,0,k,0,1);
    return ans;
}
int main()
{
    __int64 a,b;
    int k;
    memset(dp,-1,sizeof(dp));
    ten[1]=1;
    for(int i=2; i<=18; i++) ten[i]=ten[i-1]*10;
    while(scanf("%I64d%I64d%d",&a,&b,&k)!=-1)
    {
        printf("%I64d\n",solve(b,k)-solve(a-1,k));
    }
    return 0;
}


[数位dp] hdu 3967 Zero's Numberd