题目: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; }