Codeforces 55D Beautiful numbers 数位dp
题目地址:http://codeforces.com/problemset/problem/55/D
若一个数字n能被它数位上的每一个非零数字整除,称这个数为Beautiful number。
问给定区间[l, r]内有多少个Beautiful numbers。
1≤l≤r≤9*1e18
很明显的数位dp。
我觉着数位dp其实就是记忆化搜索的一种使用形式。
dfs统计小于x的Beautiful numbers的数目。
搜索过程中传递4个信息:当前位pos,搜索路径上的数字组成的数值(比如说1->3->5那么就是135),搜索路径上的数字的最小公倍数,和一个用于标志最高位应当是9还是x[pos]的flag
观察到1-9的最小公倍数为2520,1-9中数的组合的最小公倍数只有48个。于是传递数值的时候只需传递值%2520,最小公倍数的编号。
从而不同的状态数数目不超过[20][2520][48]。
AC代码:
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;++i) long long l,r; int bit[20]; long long dp[20][2520][50]; int fact[50]; int cnt; void init() { memset(dp,-1,sizeof(dp)); } int gcd(int a,int b) { return b==0?a:gcd(b,a%b); } int lcm(int a,int b) { return a*b/gcd(a,b); } void pre() { cnt=0; rep(i,1,2520) { if(2520%i==0) fact[++cnt]=i; } } int searchnum(int x) { int l=1,r=cnt; while(l<r) { int mid=(l+r)>>1; if(x<=fact[mid]) r=mid; else l=mid+1; } //printf("l=%d x=%d fact[]=%d\n",l,x,fact[l]); return l; } long long dfs(int pos,int mod,int prelcm,int flag) { if(pos==-1) { if(mod%fact[prelcm]==0) return 1; else return 0; } if(!flag&&dp[pos][mod][prelcm]!=-1) return dp[pos][mod][prelcm]; int endbit=flag?bit[pos]:9; long long res=0; int nowlcm=prelcm; rep(i,0,endbit) { if(i) { nowlcm=searchnum(lcm(i,fact[prelcm])); } res+=dfs(pos-1,(mod*10+i)%2520,nowlcm,flag&&(i==endbit)); } if(!flag) dp[pos][mod][prelcm]=res; return res; } long long calc(long long x) { int len=0; while(x) { bit[len++]=x%10; x/=10; } return dfs(len-1,0,1,1); } int main() { int TT;scanf("%d",&TT); init(); pre(); rep(t1,1,TT) { scanf("%I64d%I64d",&l,&r); printf("%I64d\n",calc(r)-calc(l-1)); } return 0; }