51nod1232 完美数 数位DP

如果一个数能够被组成它的各个非0数字整除,则称它是完美数。例如:1-9都是完美数,10,11,12,101都是完美数,但是13就不是完美数(因为13不能被数字3整除)。
现在给定正整数x,y,求x和y之间(包含x和y的闭区间)共有多少完美数。

一个数能整除他的所有位上的数,也就是说要整除所有位置上的数字的lcm。
那么设f[i][j][k]表示做到第i位,模2520意义下数字为j,lcm为第k个的数字个数。2520是1-9中最大的lcm,那么预处理2520以内所有的lcm,对于相同的lcm明显可以分为一组。
然后直接记忆化搜索,枚举前一位上的数字即可。

#include
#include
#include
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=1e5+5;
const int mo=2520;
typedef long long ll;
int a[50],Lcm[N],num[N];
ll f[20][3000][50];
inline void pre()
{
    int tot=0;
    fo(i,1,2520)
    if (2520%i==0)Lcm[i]=++tot;
}
inline int gcd(int a,int b)
{
    if (a%b==0)return b;
    else return gcd(b,a%b);
}
inline int getlcm(ll x,ll y)
{
    return x*y/gcd(x,y);
}
inline ll dfs(int i,int p,int lcm,bool bz)
{
    if (i==-1)return p%lcm==0;
    if (!bz&&f[i][p][Lcm[lcm]]!=-1)
        return f[i][p][Lcm[lcm]];
    ll ans=0;
    int lim=bz?num[i]:9;
    fo(x,0,lim)
    {
        int lcm1=lcm;
        if (x>0)lcm1=getlcm(lcm1,x);
        ans=ans+dfs(i-1,(p*10+x)%2520,lcm1,bz&&x==lim);
    }
    if (bz)return ans;
    else return f[i][p][Lcm[lcm]]=ans; 
}
ll solve(ll x)
{
    int t=0;
    while(x)
    {
        num[t++]=x%10;
        x/=10;
    }
    return dfs(t-1,0,1,1);
}
int main()
{
    int cas;
    scanf("%d",&cas);pre();
    memset(f,-1,sizeof(f));
    while (cas--)
    {
        ll x,y;
        scanf("%lld%lld",&x,&y);
        printf("%lld\n",solve(y)-solve(x-1));
    }
}

你可能感兴趣的:(51nod,数位DP)