Codeforces 55D - Beautiful numbers - [数位DP+离散化]
题目链接:https://cn.vjudge.net/problem/CodeForces-55D
Volodya is an odd boy and his taste is strange as well. It seems to him that a positive integer number is beautiful if and only if it is divisible by each of its nonzero digits. We will not argue with this and just count the quantity of beautiful numbers in given ranges.
Input
The first line of the input contains the number of cases t (1 ≤ t ≤ 10). Each of the next t lines contains two natural numbers li and ri (1 ≤ li ≤ ri ≤ 9 ·1018).
Please, do not use %lld specificator to read or write 64-bit integers in C++. It is preffered to use cin (also you may use %I64d).
Output
Output should contain t numbers — answers to the queries, one number per line — quantities of beautiful numbers in given intervals (from li to ri, inclusively).
Example
1
1 9
9
1
12 15
2
题解:
首先,有 X % a = [ X % ( b * a ) ] % a,其中a,b,X均为正整数;
证明:设X = m * a + n;(其中n<a)
则 [ X % ( b * a ) ] % a
= [ ( m * a + n ) % ( b * a ) ] % a
= [ ( m * a ) % ( b * a ) + n % ( b * a ) ] % a
= [ a * ( m % b ) + n % ( b * a ) ] % a
= [ n % ( b * a ) ] % a (因为n<a,所以必然n<b*a,所以n % ( b * a ) = n)
= n % a (因为n<a,所以n % a = n)
= n = X % a
当我们想判断一个数是否能被它的所有位上的非零数字整除时,可以判断这个数是否能被它的所有位上的非零数字的最小公倍数整除;
也就是说,我们有一个数X,a = lcm( all non-zero digit(X) ),我们要判断X % a == 0?
就可以先想出一个数字b * a,让X先对 b * a 取模,再对a取模判断,结果是一样的;
那么哪个数字b * a是通用的呢?显然,lcm(1,2,3,…,9)就是最佳人选。
经计算lcm(1,2,3,…,9) = 2520,
也就是说,我们可以先在dfs过程中计算出某一个数X对2520的取模值mod,同时计算出某一个数X的所有数位上的数的最小公倍数lcm;
最后我们只要判断mod % lcm == 0?即可判断X是否整除lcm。
所以就不难设计出dfs(pos,mod,lcm,limit)函数;
然后,我们发现dp[pos][mod][lcm],规模约有4*(19*2520*2520) = 4*120657600 B ≈ 471318.75 KB,题目并不允许开这么大的空间,所以需要离散化;
不难发现,某一个数X的所有数位上的数的最小公倍数lcm,其必须是2520的因数,而我们通过如下代码:
void discretize() { int cnt=0; for(int i=1;i<=MOD;i++) if(MOD % i == 0) ++cnt; cout<<cnt<<endl; }
就能知道1~2520中只有48个数,是2520的因数。
然后,我们只要把这48个数,离散化编号即可,这样dp数组lcm维度就可以从2520降到48,就不会超出题目规定的内存空间。
AC代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int MOD = 2520; int dig[20]; int idx[MOD+3]; ll dp[20][2525][50]; inline ll gcd(ll m,ll n){return n?gcd(n,m%n):m;} inline ll lcm(ll m,ll n){return m/gcd(m,n)*n;} void discretize() { int cnt=0; for(int i=1;i<=MOD;i++) if(MOD % i == 0) { idx[i]=++cnt; //cout<<"idx["<<i<<"]="<<idx[i]<<endl; } //cout<<cnt<<endl; } ll dfs(int pos,int mod,int Lcm,bool limit) { if(pos==0) return mod%Lcm==0; if(!limit && dp[pos][mod][idx[Lcm]]!=-1) return dp[pos][mod][idx[Lcm]]; int up=limit?dig[pos]:9; ll ans=0; for(int i=0;i<=up;i++) { if(i==0) ans+=dfs(pos-1,(mod*10+i)%MOD,Lcm,limit && i==up); else ans+=dfs(pos-1,(mod*10+i)%MOD,lcm(Lcm,i),limit && i==up); } if(!limit) dp[pos][mod][idx[Lcm]]=ans; return ans; } ll solve(ll x) { int len=0; while(x) { dig[++len]=x%10; x/=10; } return dfs(len,0,1,1); } int main() { discretize(); int t; scanf("%d",&t); memset(dp,-1,sizeof(dp)); while(t--) { ll l,r; scanf("%I64d%I64d",&l,&r); printf("%I64d\n",solve(r)-solve(l-1)); } }