codeforces 55D D. Beautiful numbers(数位dp+数论)

题目链接:

codeforces 55D

题目大意:

求在[l,r]中能够整除自己每个数位上的数字的数的个数。

题目分析:

  • 首先我们能够知道如果这个数能够整除它的每个数位上的数字,那么它一定能够整除他们的最小公倍数,是充要的。
  • 那么我们定义状态dp[i][j][k]代表i位在任意组合下得到的所有数位的数字的最小公倍数为j的每个数位上的数字之积%2520为k的方案数。
  • 我们可以知道所有的公倍数最大不会超过2520,而且他们都是2520的约数,所以如果他们能够整除2520的余数,那么证明他们能够整除所有数位上的数字之积,是充要的。
  • 所以我们利用这样状态记录进行记忆化搜索,对于给定的数字求取比它小的所有的符合要求的数,间接的求出区间内合法的数的个数,具体求法就是对于给定数的每一位,枚举它可能的数字,如果小于当前位i的数字,那么后面方案数就是dp[n-i][j][k],找出满足条件的j和k即可,利用深搜来实现。如果等于当前位,那么固定当前位,更新lcm,然后转移到下一位去寻找方案数。
  • 清楚了状态就是一个简单的枚举和统计的操作了。具体看代码吧….写的不清楚的地方欢迎评论指正。

AC代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>

using namespace std;

typedef long long LL;

LL dp[20][50][3000];
int d[20],hash[3000];
const int mod = 2520;

int gcd ( int a ,int b )
{
    return !b?a:gcd(b,a%b);
}

void init ( )
{
    memset ( dp , -1 , sizeof ( dp ) );
    int cc = 0;
    for ( int i = 1 ; i <= 8 ; i *= 2 )
        for ( int j = 1; j <= 9 ; j*= 3 )
            for ( int k = 1 ; k <= 5 ; k *= 5 )
                for ( int t = 1; t <= 7 ; t *= 7 )
                    hash[i*j*k*t] = ++cc;
}

LL dfs ( int n , int lcm , int f , int r )
{
    if ( !n ) return r%lcm==0;
    int ll = hash[lcm];
    if ( f && dp[n][ll][r] != -1 ) 
        return dp[n][ll][r];
    int x = f?9:d[n];
    LL ret = 0;
    for ( int i = 0 ; i <= x ;i++ )
        ret += dfs ( n-1 , i==0?lcm:lcm*i/gcd(lcm,i) , i==x?f:1 , (r*10+i)%mod );
    return f? dp[n][ll][r] = ret : ret;
}

LL solve ( LL x )
{
    int cc = 0;
    while ( x )
    {
        d[++cc] = x%10;
        x /= 10;
    }
    return dfs ( cc , 1 , 0 , 0 );
}

int main ( )
{
    int t;
    LL a,b;
    scanf ( "%d" , &t );
    init ( );
    while ( t-- )
    {
        scanf ( "%lld%lld" , &a , &b );
        printf ( "%lld\n" , solve ( b ) - solve ( a-1 ) );
    }
}

你可能感兴趣的:(dp,数论,codeforces)