[bzoj1799][DP]self 同类分布

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Rose_max/article/details/81673682

Description

给出a,b,求出[a,b]中各位数字之和能整除原数的数的个数。

Input

Output

Sample Input

10 19

Sample Output

3

HINT

【约束条件】1 ≤ a ≤ b ≤ 10^18

题解

明明很水我做了这么久..真是失了智
容易发现模数不会大于9*18
容易发现总和不会大于9*18
我们枚举模数,记忆化搜索数位dp
分别是
当前位置 当前余数 当前模数 当前剩余数的和 前面的位数是否顶上界
裸着跑就好了…

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#define LL long long
using namespace std;
LL l,r,pre[20];
int num[20],ln;
void gt(LL p)
{
    ln=0;
    while(p)num[++ln]=p%10,p/=10;
}
LL f[20][170][170][2];
int gg[20][170][170][2],tim; 
LL dp(int pos,int ux,int md,int sum,int op)//位置 余数 模数 剩下的 顶上界
{
    if(sum<0)return 0;
    if(pos<=0)
    {
        if(ux==0&&sum==0)return 1;
        return 0;
    }
    if(gg[pos][ux][sum][op]==tim)return f[pos][ux][sum][op];
    LL s=0;
    if(op==1)//顶上界
    {
        s=s+dp(pos-1,(ux-num[pos]*pre[pos]%md+md)%md,md,sum-num[pos],1);//顶上界 
        for(int i=0;i<num[pos];i++)
            s=s+dp(pos-1,(ux-i*pre[pos]%md+md)%md,md,sum-i,0);
    }
    else
    {
        for(int i=0;i<=9;i++)   
        {
    //      printf("%lld\n",dp(pos-1,(ux-i*pre[pos]%md+md)%md,md,sum-i,0));
            s=s+dp(pos-1,(ux-i*pre[pos]%md+md)%md,md,sum-i,0);
        }
    }
    gg[pos][ux][sum][op]=tim;f[pos][ux][sum][op]=s;
    return s;
}
LL S(LL p)
{
    LL ret=0;
    gt(p);
    for(int i=1;i<=9*ln;i++)
        tim++,ret+=dp(ln,0,i,i,1);
    return ret;
}
int main()
{
    pre[1]=1;for(int i=2;i<=18;i++)pre[i]=pre[i-1]*10;
    scanf("%lld%lld",&l,&r);
    printf("%lld\n",S(r)-S(l-1));
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Rose_max/article/details/81673682
今日推荐