https://vjudge.net/contest/70324
A - Beautiful numbers
统计区间内的,被数位上各个为零数字整除的数的个数。
暴力的数位dp写法,绝对会TLE的,因为这个要深入到每个数字的最后才能判断是否合法。因为记忆化的意义在询问不多的时候用处不大就去掉了。
#include<bits/stdc++.h> using namespace std; #define ll long long int a[20]; int d[10]; ll dfs(int pos,ll sum,bool lead,bool limit) { //递归边界,最低位是0,那么pos==-1说明这个数枚举完了 if(pos==-1) { for(int i=1; i<=9; i++) { if(d[i]) { if(sum%i) return 0; } } return 1; } int up=limit?a[pos]:9;//根据limit判断枚举的上界up ll ans=0; //开始计数 for(int i=0; i<=up; i++) { //枚举,然后把不同情况的个数加到ans就可以了 //合法的状态向下搜索 if(i) d[i]++; ans+=dfs(pos-1,sum*10ll+i,lead && i==0,limit && i==a[pos]);//最后两个变量传参都是这样写的 if(i) d[i]--; } return ans; } ll solve(ll x) { //可能需要特殊处理0或者-1 if(x<=0) return 1; int pos=0; while(x) { //把数位分解 a[pos++]=x%10;//编号为[0,pos),注意数位边界 x/=10; } memset(d,0,sizeof(d)); return dfs(pos-1,0,true,true);//刚开始最高位都是有限制并且有前导零的,显然比最高位还要高的一位视为0嘛 } int main() { int t; scanf("%d",&t); ll le,ri; while(t--) { scanf("%lld%lld",&le,&ri); printf("%lld\n",solve(ri)-solve(le-1)); } }
没想出来怎么解决,去查了题解,题解暗示说,这样是和最小公倍数有关的。好像的确很有道理,细节只能自己想了。
首先考虑1~9的最小公倍数,也就是 $1*2^3*3^2*5*7=2520$ ,题解提到一个充要条件,就是一个数假如要能被某些数整除,等价于被这些数的最小公倍数整除,这个充要条件的正确性可以由质因数分解得知,就是说这个数的质因数分解必须比他的各个数位的质因数分解“高”,也就比各个数位的质因数分解的“轮廓”也就是最小公倍数“高”。