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\n", calc(r) - calc(l-1));
90     }
91     return 0;
92 }

时间: 2024-10-05 04:46:26

codeforces55D数位dp的相关文章

CodeForces - 55D - Beautiful numbers(数位DP,离散化)

链接: https://vjudge.net/problem/CodeForces-55D 题意: Volodya is an odd boy and his taste is strange as well. It seems to him that a positive integer number is beautiful if and only if it is divisible by each of its nonzero digits. We will not argue with

51Nod 1009 数字1的个数 | 数位DP

题意: 小于等于n的所有数中1的出现次数 分析: 数位DP 预处理dp[i][j]存 从1~以j开头的i位数中有几个1,那么转移方程为: if(j == 1) dp[i][j] = dp[i-1][9]*2+pow(10,i-1);else dp[i][j] = dp[i-1][9]+dp[i][j-1]; 然后注意下对于每个询问统计的时候如果当前位为1需要额外加上他后面所有位数的个数,就是n%pow(10,i-1); 这样总复杂度log(n)*10 #include <bits/stdc++.

HDU 3555 Bomb (数位DP)

数位dp,主要用来解决统计满足某类特殊关系或有某些特点的区间内的数的个数,它是按位来进行计数统计的,可以保存子状态,速度较快.数位dp做多了后,套路基本上都差不多,关键把要保存的状态给抽象出来,保存下来. 简介: 顾名思义,所谓的数位DP就是按照数字的个,十,百,千--位数进行的DP.数位DP的题目有着非常明显的性质: 询问[l,r]的区间内,有多少的数字满足某个性质 做法根据前缀和的思想,求出[0,l-1]和[0,r]中满足性质的数的个数,然后相减即可. 算法核心: 关于数位DP,貌似写法还是

51nod1043(数位dp)

题目链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1043 题意:中文题诶- 思路:数位dp 我们用dp[i][j]来存储长度为2*i且一半和为j的所有情况(包括前导0的情况),为了方便我们现在只讨论其一半的和的情况,因为如果包括前导0的话其两边的情况是一样的: 我们假设再长度为i-1的数字最前面加1位数字k,0<=k<=9(这位数字加在哪里并不影响答案,因为我们在计算i-1长度的时候已经计算了所有组合情况,

数位dp

1.[hdu3709]Balanced Number 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<string> 5 #include<cstdlib> 6 #include<algorithm> 7 #include<ctime> 8 #include<cmath> 9 #include<queue>

【HDU 3652】 B-number (数位DP)

B-number Problem Description A wqb-number, or B-number for short, is a non-negative integer whose decimal form contains the sub- string "13" and can be divided by 13. For example, 130 and 2613 are wqb-numbers, but 143 and 2639 are not. Your task

hdu 5898 odd-even number 数位DP

odd-even number Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 716    Accepted Submission(s): 385 Problem Description For a number,if the length of continuous odd digits is even and the length

LightOJ1068 Investigation(数位DP)

这题要求区间有多少个模K且各位数之和模K都等于0的数字. 注意到[1,231]这些数最大的各位数之和不会超过90左右,而如果K大于90那么模K的结果肯定不是0,因此K大于90就没有解. 考虑到数据规模,数据组数,这题状态这么表示: dp[i][j][k]:位数为i模K结果为j且各位数之和模K结果为k的数字个数 然后就是转移方程,最后就是统计.. 统计部分好棘手...半乱搞下AC的..还是对数位DP的这一部分太不熟悉了. 1 #include<cstdio> 2 #include<cstr

HDU 4734 F(x)(数位DP)

Description For a decimal number x with n digits (A nA n-1A n-2 ... A 2A 1), we define its weight as F(x) = A n * 2 n-1 + A n-1 * 2 n-2 + ... + A 2 * 2 + A1 * 1. Now you are given two numbers A and B, please calculate how many numbers are there betwe