传送门:Chriswho
题意:求区间[1,n]内能整除自己本身各位数字的数的个数。
分析:这题跟CF 55D Beautiful numbers一样的,一个数能被它的所有非零数位整除,则能被它们的最小公倍数整除,而1到9的最小公倍数为2520,为了判断这个数能否被它的所有数位整除,我们还需要这个数的值,但这里我们只需记录它对2520的模即可,dp[pos][sum][lcm]表示非限制条件下(limit==0),当前在第pos位模2520余sum且前面各位数字的最小公倍数为lcm的符合条件的数的总数。
#include <cstdio> #include <cstring> #include <string> #include <cmath> #include <iostream> #include <algorithm> #include <queue> #include <cstdlib> #include <stack> #include <vector> #include <set> #include <map> #define LL long long #define mod 100000000 #define inf 0x3f3f3f3f #define eps 1e-6 #define N 1010 #define FILL(a,b) (memset(a,b,sizeof(a))) #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define PII pair<int,int> using namespace std; LL dp[20][2525][50]; int dig[20],num[2525]; LL times=0; int gcd(int a,int b) { return a%b==0?b:gcd(b,a%b); } LL dfs(int pos,int sum,int lcm,int limit) { if(!pos){//times++; return sum%lcm==0;} if(!limit&&~dp[pos][sum][num[lcm]])return dp[pos][sum][num[lcm]]; int len=limit?dig[pos]:9; LL ans=0; for(int i=0;i<=len;i++) { int newlcm; if(i==0)newlcm=lcm; else newlcm=lcm/gcd(i,lcm)*i; ans+=dfs(pos-1,(sum*10+i)%2520,newlcm,limit&&len==i); } if(!limit)dp[pos][sum][num[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 check(LL n) { LL x=n,flag=1; while(x) { int s=x%10; x/=10; if(s==0)continue; if(n%s)flag=0; } return flag; } LL fact(LL x) { LL res=0; for(int i=1;i<=x;i++) { if(check(i))res++; } return res; } void init() { FILL(dp,-1); int cnt=0; for(int i=1;i<=2520;i++) if(2520%i==0)num[i]=++cnt; } int main() { LL n; int T; init(); scanf("%d",&T); while(T--) { scanf("%I64d",&n); printf("%I64d\n",solve(n)-1); // printf("%I64d\n",fact(b)-fact(a-1)); // printf("%I64d\n",times); } }