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

来源:互联网 发布:合肥唯米淘宝培训 编辑:程序博客网 时间:2024/04/30 21:01

题目链接:

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 ) );    }}
0 0
原创粉丝点击