FZU2179(数位dp)

时间:2021-12-01 20:20:27

传送门: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[][][];
int dig[],num[];
LL times=;
int gcd(int a,int b)
{
return a%b==?b:gcd(b,a%b);
}
LL dfs(int pos,int sum,int lcm,int limit)
{
if(!pos){//times++;
return sum%lcm==;}
if(!limit&&~dp[pos][sum][num[lcm]])return dp[pos][sum][num[lcm]];
int len=limit?dig[pos]:;
LL ans=;
for(int i=;i<=len;i++)
{
int newlcm;
if(i==)newlcm=lcm;
else newlcm=lcm/gcd(i,lcm)*i;
ans+=dfs(pos-,(sum*+i)%,newlcm,limit&&len==i);
}
if(!limit)dp[pos][sum][num[lcm]]=ans;
return ans;
}
LL solve(LL x)
{
int len=;
while(x)
{
dig[++len]=x%;
x/=;
}
return dfs(len,,,);
}
int check(LL n)
{
LL x=n,flag=;
while(x)
{
int s=x%;
x/=;
if(s==)continue;
if(n%s)flag=;
}
return flag;
}
LL fact(LL x)
{
LL res=;
for(int i=;i<=x;i++)
{
if(check(i))res++;
}
return res;
}
void init()
{
FILL(dp,-);
int cnt=;
for(int i=;i<=;i++)
if(%i==)num[i]=++cnt;
}
int main()
{
LL n;
int T;
init();
scanf("%d",&T);
while(T--)
{
scanf("%I64d",&n);
printf("%I64d\n",solve(n)-);
// printf("%I64d\n",fact(b)-fact(a-1));
// printf("%I64d\n",times);
}
}