题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1799

   https://www.luogu.org/problemnew/show/P4127

经典dp!

一个数能被它的各位和整除,在L-R内有多少个。

1.数位dp的套路,先预处理出第i位、后面任意的所有情况。

  因为涉及整除,所以状态有“模当前数余几”;

  因为涉及各位和,所以状态有“i位和为j”和“模k”;

好了我们有了一份会MLE的四维代码,而且有会超时的18位预处理,答案好歹是正确的。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int sx=18*9;
ll tmp,f[19][165][165][165],cnt1,cnt2;
int lmm,a[19];
ll pw(int k)
{
    ll mul=1;
    for(int i=1;i<=k;i++)
        mul*=10;
    return mul;
}
void pre()
{
    for(int k=1;k<=sx;k++)
            f[0][0][k][0]=1;
    int lm=0;
    for(int i=1;i<=18;i++)
    {
        lm=i*9;
        for(int j=0;j<=lm;j++)
            for(int k=1;k<=sx;k++)
                for(int l=0;l<k;l++)
                    for(int p=0;p<=9&&j-p>=0;p++)
                        f[i][j][k][l]+=f[i-1][j-p][k][((l-p*pw(i-1))%k+k)%k]; 
//                        if(i==1&&j==p&&k==p)printf("j=%d k=%d l=%d p=%d ff=%lld\n",j,k,l,p,f[i][j][k][l]);
                        
    }
}
void chl()
{
    lmm=0;
    while(tmp)
    {
        a[++lmm]=tmp%10;
        tmp/=10;
    }
}
ll calc()
{
    int t=0;
    ll cnt=0,lj=0;
    for(int i=lmm;i;i--)
    {
        int ll=a[i];if(i==1)ll++;
        for(int p=0;p<ll;p++)
            for(int s=max(1,t+p);s<=sx;s++)
                cnt+=f[i-1][s-t-p][s][(s-(lj+p*pw(i-1))%s)%s]; 
        t+=a[i];lj+=a[i]*pw(i-1);
    }
    return cnt;
}
int main()
{
    scanf("%lld",&tmp);tmp--;
    pre();
    chl();
    cnt1=calc();
//    printf("(%lld)\n",cnt1);
    scanf("%lld",&tmp);
    chl();
    cnt2=calc();
//    printf("(%lld)\n",cnt2);
    printf("%lld",cnt2-cnt1);
    return 0;
}
MLE+TLE

相关文章: