codeforces-55d-Beautiful numbers-数位dp

题目链接

好吧,上次wtw菊苣告诉我把kuangbin系列的数位dp做一遍,第一题就感觉很难啊(好吧,主要太水了)……
题意:求在给定区间内,能被所有自己的非零位整除的数的个数

思路: 因为这个题目需要整除,所以肯定要记录当前数的一些信息来保证整除性,除数可以用所有位数的最小公倍数来记录,最多也就是2520,离散化之后就几十个。被除数的话,可以记录它mod2520的值,设现在已经构造的数是2520x+y,被除数是lcm(可以得出2520%lcm=0),那么被除数除上除数就是2520x/lcm+y/lcm,所以整除性可以只看y。
那么这个dp就是第一维位数pos,第二维当前数mod2520的值,第三维各个数位的lcm,第四维边界条件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
int cnt[100];
long long dp[30][2524][50];
int dig[30];
int mod=2520;
int cntt=0;
int gcd(int a,int b)
{
return b==0?a:gcd(b,a%b);
}
int lcm(int a,int b)
{
return a/gcd(a,b)*b;
}
long long dfs(int pos,int mo,int lcmid,int inf)
{
if(pos<1)return mo%cnt[lcmid]==0?1:0;
if(!inf && dp[pos][mo][lcmid]!=-1)return dp[pos][mo][lcmid];
int endl=inf==1?dig[pos]:9;
long long rt=0;
for(int i=0;i<=endl;i++)
{ int lcmm;
int mmo=(mo*10+i)%2520;
if(i!=0)lcmm=lcm(cnt[lcmid],i);
else lcmm=cnt[lcmid];
int poss=lower_bound(cnt+1,cnt+1+cntt,lcmm)-cnt;
rt+=dfs(pos-1,mmo,poss,inf && i==endl);
}
if(!inf) dp[pos][mo][lcmid]=rt;
return rt;
}
int main()
{
int t;
for(int i=1;i<=2520;i++)
if(2520%i==0)cnt[++cntt]=i;
// for(int i=1;i<=48;i++)printf("%d\n",cnt[i]);
memset(dp,-1,sizeof(dp));
scanf("%d",&t);
while(t--)
{ long long x,y;
scanf("%I64d%I64d",&x,&y);
memset(dig,0,sizeof(dig));
x--;
long long ans1;
long long ans2;
int cc=0;
while(x>0)
{
dig[++cc]=x%10;
x/=10;
}
ans1=dfs(cc,0,1,1);
memset(dig,0,sizeof(dig));
cc=0;
while(y>0)
{
dig[++cc]=y%10;
y/=10;
}
ans2=dfs(cc,0,1,1);
printf("%I64d\n",ans2-ans1);
}
return 0;
}