zoukankan      html  css  js  c++  java
  • codeforces55D_数位dp

    题目链接

    参考链接:http://blog.csdn.net/to_be_better/article/details/50658392

    题意:

    求一个区间内的Beautiful numbers有多少个。Beautiful numbers指:一个数能整除所有组成它的非0数字。 
    例如15可以被1和5整除,所以15是Beautiful numbers。

    思路

    Beautiful numbers指:一个数能整除所有组成它的非0数字。 
    等同于 一个数能整除 所有组成它的非0数字的最小公倍数。 
    我们看到数据的范围是1 ≤ li ≤ ri ≤ 9 ·1018,根本无法记录。 
    所以,缩小范围成为第一要做的事。 
    先看一个小式子。

        sum%(x*n)%x == sum%x;
    
    证明:设sum = k*x+b
        等号左边:
            sum%(x*n)%x -> (k*x+b)%(x*n)%x 
            将k转为ka*n + kb代入;
            (ka*n*x+kb*x+b)%(x*n)%x -> (kb*x+b)%x -> b%x -> b
        等号右边:
            b
    左右相等,证明成立
    那么我们就可以用上式中的x*n对num进行取余,记录其取余后的值,显然,1~9的最小公倍数2520是最合理的x*n。 
    而在逐位统计时,可以直接由前面位取余后的值来得到包含新一位的新数字取余后的值。 
    例如 RX(R是已知前面位取余后的值),那么Rx%2520 == (R*10+x)%2520。就不在此废话证了。 
    我们使用记忆化搜索。 
    **dfs(len, num, lcm, flag) 
    len表示迭代的长度, 
    num为截止当前位的数对2520取余后的值。 
    lcm为截止当前位的所有数的最小公倍数。 
    flag表示当前数是否可以任意取值(对取值上限进行判断)** 
    则可用dp[len][num][lcm]来对其进行记录。 
    但lcm按2520取值根本开不下,所以对lcm进行离散化,因为lcm一定可以整除2520,所以将1~2520可以整除2520的数进行标记即可,测试后发现只有48个,满足当前情况。
     1 #include <algorithm>
     2 #include <iostream>
     3 #include <cstring>
     4 #include <cstdlib>
     5 #include <cstdio>
     6 #include <vector>
     7 #include <ctime>
     8 #include <queue>
     9 #include <list>
    10 #include <set>
    11 #include <map>
    12 using namespace std;
    13 #define INF 0x3f3f3f3f
    14 typedef long long LL;
    15 
    16 LL dp[20][50][2550];
    17 int fact[2550], bit[20];
    18 void find()
    19 {
    20     memset(fact, 0, sizeof(fact));
    21     int cnt = 0;
    22     for(int i = 1;  i <= 2520; i++)
    23         if(2520 % i == 0)
    24             fact[i] = cnt++;
    25 }
    26 LL dfs(int len, int lcm, int mod, int flag)
    27 {
    28     LL sum = 0;
    29     if(len == 0)
    30         return mod % lcm == 0;
    31     if(flag && dp[len][fact[lcm]][mod] >= 0)
    32         return dp[len][fact[lcm]][mod];    
    33     int te = flag ? 9 : bit[len];
    34     for(int i = 0; i <= te; i++)
    35     {
    36         int Lcm = lcm;
    37         if(i != 0){
    38             int x = __gcd(lcm, i);
    39             Lcm = lcm * i / x;
    40         }
    41         int Mod = (i + mod * 10) % 2520;
    42         sum += dfs(len - 1, Lcm, Mod, flag || i < te);
    43     }
    44     if(flag)
    45         dp[len][fact[lcm]][mod] = sum;
    46     return sum;
    47 }
    48 LL solve(LL n)
    49 {
    50     int len = 0;
    51     while(n)
    52     {
    53         bit[++len] = n % 10;
    54         n /= 10;
    55     }
    56     return dfs(len, 1, 0, 0);
    57 }
    58 int main()
    59 {
    60     int t;
    61     find();
    62     LL l, r;
    63     scanf("%d", &t);
    64     memset(dp, -1, sizeof(dp));
    65     while(t--)
    66     {
    67         scanf("%I64d %I64d", &l ,&r);
    68         printf("%I64d
    ", solve(r) - solve(l - 1));
    69     }
    70     return 0;
    71 }
    View Code
     
  • 相关阅读:
    [ jquery 选择器 :hidden ] 此方法选取匹配所有不可见元素,或者type为hidden的元素
    剑指 Offer 03. 数组中重复的数字 哈希
    LeetCode 1736. 替换隐藏数字得到的最晚时间 贪心
    Leetcode 1552. 两球之间的磁力 二分
    Leetcode 88. 合并两个有序数组 双指针
    LeetCode 1744. 你能在你最喜欢的那天吃到你最喜欢的糖果吗?
    LeetCode 1743. 相邻元素对还原数组 哈希
    LeetCode 1745. 回文串分割 IV dp
    剑指 Offer 47. 礼物的最大价值 dp
    剑指 Offer 33. 二叉搜索树的后序遍历序列 树的遍历
  • 原文地址:https://www.cnblogs.com/luomi/p/5777731.html
Copyright © 2011-2022 走看看