• codeforces55D数位dp


    codeforces55D

    查询给定区间内的beautiful number。  一个数字是beautiful number当且仅当能被自己的各个数字不为0的位整除。

    这个dp的状态还是挺难想的。一个数能被自己的各个位整除,那么必须是这些位的最小公倍数的倍数。

    那么可以想到的一个状态是dp[i][j][k] 为长度为i的时候,数字是组成的数字是j,各个位的最小公倍数是k, 那么当j%k==0,说明数字j是可行的。

    当时j太大了,根本存不下。但是数字1-->9最大的一个最小公倍数是2520, 所以只要 j%2520%k==0就行了。

    这是为什么呢 ,因为j可以分解为(k*2520 + x) % k , x=j%2520, 所以只要判断x%k是不是等于0就行了。

     1 #include <stdio.h>
     2 #include <string.h>
     3 #include <stdlib.h>
     4 #include <algorithm>
     5 #include <iostream>
     6 #include <queue>
     7 #include <stack>
     8 #include <vector>
     9 #include <map>
    10 #include <set>
    11 #include <string>
    12 #include <math.h>
    13 using namespace std;
    14 #pragma warning(disable:4996)
    15 #pragma comment(linker, "/STACK:1024000000,1024000000")
    16 typedef __int64 LL;      
    17 const int mod = 2520;
    18 const int INF = 1<<30;
    19 /*
    20 dp[i][j][k]
    21 表示前取了前i位,数字为j,各个位的最小公倍数位k
    22 如果j%k==0  那么说明可以
    23 
    24 dp[i+1][j*10 + i][lcm(k,i)] = dp[i][j][k]
    25 
    26 */
    27 
    28 LL dp[30][2520][50];
    29 int num[64];
    30 bool vis[10000];
    31 int hs[2520 + 10];
    32 int gcd(int a, int b)
    33 {
    34     if (b == 0)return a;
    35     return gcd(b, a%b);
    36 }
    37 int lcm(int a, int b)
    38 {
    39     return a*b / gcd(a, b);
    40 }
    41 void init()
    42 {
    43     int cnt = 0;
    44     for (int i = 1; i <= mod; ++i)
    45     if (mod%i == 0)
    46         hs[i] = cnt++;
    47 }
    48 
    49 LL dfs(int pos, int preSum, int preLcm, bool flag)
    50 {
    51     if (pos == 0)return preSum % preLcm==0;
    52     if (!flag && dp[pos][preSum][hs[preLcm]] != -1)
    53         return dp[pos][preSum][hs[preLcm]];
    54     int limit = flag ? num[pos] : 9;
    55     LL ans = 0;
    56     for (int i = 0; i <= limit; ++i)
    57     {
    58         int nowSum = (preSum * 10 + i) % mod;
    59         int nowLcm = preLcm;
    60         if (i)nowLcm = lcm(nowLcm, i);
    61         ans += dfs(pos - 1, nowSum, nowLcm, flag&&i == limit);
    62     }
    63     if (!flag)//如果pos为比n的第pos位小,那么说明可以利用已有的结果,如果不是的话,不能,因为可能比n大
    64         dp[pos][preSum][hs[preLcm]] = ans;
    65     return ans;
    66 }
    67 LL calc(LL n)
    68 {
    69 
    70     int len = 0;
    71     while (n)
    72     {
    73         num[++len] = n % 10;
    74         n /= 10;
    75     }
    76     
    77     return dfs(len,0,1,1);
    78 }
    79 int main()
    80 {
    81     init();
    82     int t;
    83     LL l, r;
    84     memset(dp, -1, sizeof(dp));//只初始化一次,因为计算的结果可多次利用
    85     scanf("%d", &t);
    86     while (t--)
    87     {
    88         scanf("%I64d%I64d", &l, &r);
    89         printf("%I64d
    ", calc(r) - calc(l-1));
    90     }
    91     return 0;
    92 }
    View Code
  • 相关阅读:
    Count on a Tree II
    DZY Loves Math
    二次剩余
    exCRT & 骆克强乘法
    CF 585 E Present for Vitalik the Philatelist
    Dirichlet 前缀和的几种版本
    51nod 1630(定积分 + 期望)
    Atcoder刷题小记
    3194. 【HNOI模拟题】化学(无标号无根树计数)
    3754. 【NOI2014】魔法森林(LCT)
  • 原文地址:https://www.cnblogs.com/justPassBy/p/4712500.html
Copyright © 2020-2023  润新知