CodeForces

题目链接: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;
    }
}

  

原文地址:https://www.cnblogs.com/zcb123456789/p/13644909.html