数位dp CF 55 D. Beautiful numbers

题目链接:

http://codeforces.com/problemset/problem/55/D

 

题目大意:

求区间内满足能被每位非零数整除数的个数。数据范围:9*10^18

 

解题思路:

lcm(1,2,3,4,5,6,7,8,9)=2^3*3^2*5*7=2520  公约数的总个数为4*3*2*2=48个

a%b=0 可推出 a%(k*b)%b=0

故可以用记忆化搜索 dfs(cur,mod,llcm,flag) ;表示从个位到cur位,之前的%2520为mod,之前的各位最小公倍数为llcm,flag=1,表示之前全部是最高位,flag=0表示之前的不全是最高位(也就是一般情况)

用二分查找该最小公倍数的位置(离散化处理2520内的可能的约数)

 

代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define eps 1e-6
#define INF (1<<30)
#define PI acos(-1.0)
using namespace std;
#define ll __int64
#define MOD  2520  //lcm(1,2,3,4,5,6,7,8,9)=2^3*3^2*5*7  公约数的总个数为4*3*2*2=48个

ll save[50];
ll dp[20][2530][50];
ll pos[20];
int cnt;

ll gcd(ll a,ll b)
{
    if(a%b==0)
        return b;
    return gcd(b,a%b);

}

ll lcm(ll a,ll b)
{
    return a/gcd(a,b)*b;
}

void init()
{
    cnt=0;
    for(int i=1;i<=MOD;i++)   //计算(1-9)内可能作为的倍数
        if(MOD%i==0)
            save[cnt++]=i;
    cnt--;
    memset(dp,-1,sizeof(dp));  //算了一次后就不用算了
    return ;
}

ll bs(ll a)
{
    int le=0,ri=cnt,mid;

    while(le<=ri)   //二分查找 该公倍数的位置
    {
        mid=(le+ri)>>1;

        if(a==save[mid])
            return mid;  //一定会找到的

        if(a>save[mid])
           le=mid+1;
        else
            ri=mid-1;
    }

}
ll dfs(ll cur,ll mod,ll llcm,ll flag)
{
    if(cur==-1)
        return (mod%save[llcm])?0:1;  //如果能够求余就加一个

    if(!flag&&dp[cur][mod][llcm]!=-1)
        return dp[cur][mod][llcm];  //如果没有满,满的情况各个样例就不一样了,

    int Max=flag?pos[cur]:9; //如果满了的话就选择pos[cur],否则一般情况则选择9

    ll res=0;

    for(int i=0;i<=Max;i++)
    {
        ll temp=(mod*10+i)%MOD;
        ll lccm=llcm;

        if(i)
            lccm=bs(lcm(i,save[llcm]));  //计算包括当前数的最大公约数

        res+=dfs(cur-1,temp,lccm,flag&&(i==Max));
    }
    if(!flag)
        dp[cur][mod][llcm]=res;

    return res;
}

ll Cal(ll a)
{
    int num=0;

    while(a)  //分离出该数
    {
        pos[num++]=a%10;
        a/=10;
    }
    return dfs(num-1,0,0,1);
}

int main()
{
    init();
    int t;
    ll a,b;

    scanf("%d",&t);
    while(t--)
    {
        scanf("%I64d%I64d",&a,&b);
        printf("%I64d\n",Cal(b)-Cal(a-1));
    }

   return 0;
}






 

你可能感兴趣的:(动态规划)