题目链接:https://vjudge.net/problem/CodeForces-55D
题意:求l到r中有多少个数可以整除自己的每个非零数位。
思路:一个数可以整除它所有非0位数,那它要能整除所有位数的公倍数才行。然后1,2,3,.....9,10的最小公倍数是2520,然后就直接用数位dp。但开不了dp[25][2520][2520]那么大的数组,可以离散化一下,只要知道2520的因数即可。
#include<bits/stdc++.h> using namespace std; typedef long long ll; ll dp[25][2530][50]; int a[25],b[2530]; int mod=2520; int gcd(int x,int y) { while(y) { int z=x%y; x=y; y=z; } return x; } int lcm(int x,int y) { return x*y/gcd(x,y); } //len:位数 ans:数的大小 sum:公倍数 flag:是否处于上边界 ll dfs(int len,int ans,int sum,bool flag) { if(len==0) return ans%sum==0; int up=flag?a[len]:9; if(!flag&&dp[len][ans][b[sum]]!=-1) return dp[len][ans][b[sum]]; ll tmp=0; for(int i=0; i<=up; i++) { if(i==0) tmp+=dfs(len-1,(ans*10+i)%mod,sum,flag&&i==a[len]); else tmp+=dfs(len-1,(ans*10+i)%mod,lcm(sum,i),flag&&i==a[len]); } if(!flag) dp[len][ans][b[sum]]=tmp; return tmp; } ll suan(ll x) { int pos=0; while(x) { a[++pos]=x%10; x/=10; } return dfs(pos,0,1,true); } void inif() { int v=0; for(int i=1;i<=2520;i++) { if(2520%i==0) b[i]=++v; } } int main() { inif(); memset(dp,-1,sizeof(dp)); int t; cin>>t; while(t--) { ll l,r; cin>>l>>r; cout<<suan(r)-suan(l-1)<<endl; } }