题解 CF55D 【Beautiful numbers】
EricWay1024 · · 题解
这题做了半天。美丽的数的定义是“可以被自己的所有非零数位整除的数”。做数位DP,想的就是每个状态需要什么参量来表示。注意到这个定义的限制并不在选取每个数位的时候,而是在整个数字选完之后才能做一个整体判断。
很自然地,首先想到需要知道我们已经在
我们需要判断的是整个数能否被所有数位的最小公倍数整除。最理想的做法是记录状态参量包括“每一步选完后的数前缀”模“我们所选的所有数位的最小公倍数”的值。但这是不可能的,因为在选取的每一步,我们无法预知后面的步骤会选择什么,也就无法预知这个最小公倍数是几。但所幸其实只要是这个最小公倍数的倍数作为模数,我们都可以用余数来判断原数对最小公倍数的整除性。因此最后我们选取的模数相等于是“所有最小公倍数的最小公倍数”,不难发现这个虚张声势的说法等价于
理论上这道题就做完了。但是我又被卡时间了。这时候我意识到数位DP未必非要走套路:先求小于等于
#include <bits/stdc++.h>
using namespace std;
#define rep(i,from,to) for(register int i=(int)from;i<=(int)to;++i)
#define For(i,to) for(register int i=0;i<(int)to;++i)
typedef long long ll;
inline ll read(){
ll x=0; ll sign=1; char c=getchar();
while(c>'9' || c<'0') {if (c=='-') sign=-1;c=getchar();}
while(c>='0' && c<='9'){x=(x<<3)+(x<<1)+c-'0';c=getchar();}
return x*sign;
}
const ll M = 2520;
ll l, r;
ll f[20][50][2521];
int fac[2521];
int dim_l[25]; int dim_l_size;
int dim_r[25]; int dim_r_size;
int gcd(int a, int b){ return b ? gcd(b, a % b) : a; }
inline int lcm(int a, int b) { return a / gcd(a, b) * b;}
ll dp(int x, int scm, int st, int opl, int opr) {
if (x == -1) { return (st % scm) == 0; }
ll &mem = f[x][fac[scm]][st];
if (!opl && !opr && mem > -1) return mem;
ll ret = 0;
int maxx = opr ? dim_r[x] : 9;
int minx = opl ? dim_l[x] : 0;
rep(i, minx, maxx) {
ret += dp(x - 1, (i == 0) ? scm : lcm(i, scm), ((st * 10 + i) % M),
(opl & (i == minx)), (opr & (i == maxx)));
}
if(!opl && !opr) mem = ret;
return ret;
}
inline void cal_dim(ll n, int* dim, int &dim_size) {
dim_size = 0;
while(n) {
dim[dim_size++] = n % 10; n /= 10;
}
}
ll solve(ll l, ll r) {
memset(dim_l, 0, sizeof(dim_l));
memset(dim_r, 0, sizeof(dim_r));
cal_dim(l, dim_l, dim_l_size);
cal_dim(r, dim_r, dim_r_size);
memset(f, -1, sizeof(f));
return dp(dim_r_size-1, 1, 0, 1, 1);
}
int main() {
#ifdef D
freopen("CF55D.in", "r", stdin);
double TIMEA = clock();
#endif
int cnt = 0;
for(int i = 1; i <= M; ++i) {
if (M % i == 0) fac[i] = ++cnt;
}
int T; T = read();
while(T--) {
l = read(); r = read();
ll ans = solve(l, r);
cout << ans << "\n";
}
#ifdef D
double TIMEB=clock();
printf("\n# Time consumed: %.3fs.\n", (TIMEB-TIMEA)/1000);
#endif
return 0;
}