Codeforces55D-Beautiful Numbers 解题报告
Codeforces55D-Beautiful Numbers
定义“美丽的数字”为:被它自己的每一位数上的数整除的数
给定区间
[L,R] ,求有多少个美丽的数字
思路
显然的数位DP,简单拉个板子就写完了.但是定义状态的时候发现会存不下,我们考虑压缩一下他们的状态。
引入两条重要的性质:
- 如果一个数能整除它的所有数位上的数,那么它一定能整除所有这些数位上的数的最小公倍数
- 而
1 到9 的最小公倍数是2520 ,所有数可能形成的数位上所有数的最小公倍数一定是2520 的因数
因为
然而,这样设计状态出来会发现总状态数达到了
根据性质1,如果最后的数能整除
代码
#include <cstdio>
#include <cctype>
#include <cstring>
#define ll long long
#define FILE A
#define N 2520 + 1
#define M 200
inline ll read() {
char v = getchar();ll x = 0,f = 1;
while (!isdigit(v)) {if (v == '-'){f = -1;}v = getchar();}
while (isdigit(v)) {x = x * 10 + v - 48;v = getchar();}
return x * f;
}
ll dp[20][50][N],dig[N],ind[N],cnt,mod,num[N],l,r,c;ll gcd(int a,int b){return b?gcd(b,a%b):a;}
ll mylcm(int a,int b){return a/gcd(a,b)*b;}
ll dfs(ll now,ll lcm,ll sum,int limit) {
if (!now) {
return sum % lcm == 0;
}
if (!limit && dp[now][num[lcm]][sum] != -1) return dp[now][num[lcm]][sum];
ll ans = 0,ret = (limit)?dig[now]:9;
for (ll i = 0;i <= ret;++i) {
int lcm1 = i ? mylcm(lcm,i) : lcm;
int sum1 = (sum * 10 + i) % mod;
ans += dfs(now-1,lcm1,sum1,limit&&i==ret);
}
if (!limit) {
dp[now][num[lcm]][sum] = ans;
}
return ans;
}
ll work(ll x) {
int len = 0;
while (x) {
dig[++len] = x % 10;x /= 10;
}
return dfs(len,1,0,1);
}
int main() {
memset(dp,-1,sizeof(dp));
int T = read();
mod = 2520;
for (int i = 1;i <= mod;++i) {
if (mod % i == 0) {
num[i] = cnt++;
}
}
while (T--) {
ll l = read(),r = read();
printf("%lld\n",work(r) - work(l-1));
}
return 0;
}
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。