解题报告 (十四) 数位DP



数位DP 解题报告

HDU 4722 Good Numbers

链接:HDU 4722 Good Numbers
题意:如果一个数的所有位数加起来是 10 10 10 的倍数, 则称之为 g o o d   n u m b e r good \ number good number,求区间 [ l , r ] ( 0 ≤ l ≤ r ≤ 1 0 18 ) [l, r](0 \le l \le r \le 10^{18}) [l,r](0lr1018) g o o d   n u m b e r good \ number good number 的个数;

HDU 2089 不要62

链接:HDU 2089 不要62
题意:对于一个数字,如果出现 4 或者 62 ,则属于不吉利数字,给定 n , m ( 0 < n < m < 1 0 6 ) n, m(0 \lt n \lt m < 10^6) n,m(0<n<m<106),求 [ n , m ] [n, m] [n,m] 中非不吉利数字的个数。

HDU 3555 Bomb

链接:HDU 3555 Bomb
题意:给定一个 n ( 1 ≤ n ≤ 2 63 − 1 ) n(1 \le n \le 2^{63}-1) n(1n2631),求小于等于 n n n 的数字中包含 49 的数的个数。

HDU 3652 B-number

链接:HDU 3652 B-number
题意:一个数的十进制包含子串 “13” 并且能被 13 整除,则被称为 B数,求小于等于 n ( n ≤ 1 0 9 ) n(n \le 10^9) n(n109) 的 B数。

PKU 3252 Round Numbers

链接:PKU 3252 Round Numbers
题意:如果一个数的二进制表示中 0 的个数大于等于 1,则称它为 r o u n d   n u m b e r round \ number round number,给定一个区间 [ a , b ] [a,b] [a,b],求其中 r o u n d   n u m b e r round \ number round number 的个数。

HDU 4151 The Special Number

链接:HDU 4151 The Special Number
题意:一个数字的十进制下的每个位都不同,则称为 S p e c i a l   N u m b e r Special \ Number Special Number,求小于 n ( n < 1 0 8 ) n(n \lt 10^8) n(n<108) 的数的个数。

PKU 3286 How many 0’s?

链接:PKU 3286 How many 0’s?
题意:统计区间 [ a , b ] [a, b] [a,b] 中的 0 的数量。

  • 首先利用差分法,用 g ( x ) g(x) g(x) 表示 [ 0 , x ] [0, x] [0,x] 0 0 0 的数量,答案就是 g ( b ) − g ( a − 1 ) g(b) - g(a-1) g(b)g(a1)
  • 这个题可以说是没有任何显式的约束条件,但是我们在统计的时候需要注意一个很重要的点,就是前导零是不能纳入统计的。所以在定义前缀状态的时候,对于一个长度为 n n n 的数字,我们可以分为 3 类:
  • 1)状态0: n n n 个 0 的情况;
  • 2)状态1: k ( k ≤ n − 1 ) k (k \le n-1) k(kn1) 个 0 的情况;
  • 3)状态2:其它情况;
  • 状态转移如图所示:
0-9
0,n=1
0,n<>1
1-9
0-9
0
1
2
  • 对于一个数 x x x,如果前缀状态处于 状态0,那么很显然,这个值就是零,所以零的个数为1;如果处于 状态1,那么到目前为止都是前导零,不应该纳入统计;如果处于 状态2,假设第 k k k 位的零出现次数为 h ( k ) h(k) h(k), 则有:
  • h ( k ) = { 1 0 k − 1 l i m = t r u e x m o d    1 0 k − 1 + 1 l i m = f a l s e h(k) = \begin{cases} 10^{k-1} & lim = true \\ x \mod 10^{k-1} + 1 & lim = false \end{cases} h(k)={10k1xmod10k1+1lim=truelim=false
  • 利用 数位DP 进行状态转移即可。

HDU 1663 The Counting Problem

链接:HDU 1663 The Counting Problem
题意:统计区间 [ a , b ] ( 0 < a , b < 1 0 8 ) [a, b](0 \lt a, b \lt 10^8) [a,b](0<a,b<108) 中每个数字的 [ 0 , 9 ] [0, 9] [0,9] 的数量。

  • 同 PKU 3286 How many 0’s

洛谷 P2602 数字计数

链接:洛谷 P2602 数字计数
题意:统计区间 [ a , b ] ( 0 < a , b < 1 0 12 ) [a, b](0 \lt a, b \lt 10^{12}) [a,b](0<a,b<1012) 中每个数字的 [ 0 , 9 ] [0, 9] [0,9] 的数量。

  • 同 HDU 1663 The Counting Problem

洛谷 P2657 windy 数

链接:洛谷 P2657 windy 数
题意:一个数字各个相邻位之差的绝对值不小于2则称为 Windy 数,统计区间 [ a , b ] ( 0 < a , b < 1 0 9 ) [a, b](0 \lt a, b \lt 10^9) [a,b](0<a,b<109) 中的 Windy 数 个数。

  • 前缀状态定义:已经枚举的数字的前缀的最后一位
  • 状态转移:令前一位为 x x x,当前位为 y y y,状态转移发生在 ∣ x − y ∣ ≥ 2 |x - y| \ge 2 xy2 时;
  • 前导零状态:前导零状态应该和任意状态都能发生状态转移,即满足 ∣ x − y ∣ ≥ 2 |x - y| \ge 2 xy2,所以可以用 11 进行编码。

洛谷 P3413 萌数

链接:洛谷 P3413 萌数
题意:如果一个数包含长度至少为2的回文串,则称为萌数,求区间 [ l , r ] ( l ≤ r ≤ 1 0 1000 ) [l, r](l \le r \le 10^{1000}) [l,r](lr101000) 内萌数的个数模 ( 1 0 9 + 7 10^9+7 109+7)。

  • 前缀状态定义:已经枚举的数字的前缀的最后两位;不足两位的用 10 (前导零状态) 来补齐,一旦出现满足条件的回文串,则将状态变为饱和状态,用 11 编码,除了这两个特殊状态,用高位和低位来分别表示最后第二位 和 最后第一位。所以状态定义为:
          [10]          表示前导零状态 
          [11]          表示饱和状态
     [10][0-9]          表示一位数字 
    [0-9][0-9]          表示两位以上数字的最后两位
  • 状态转移实现如下:
const int saturatedstate = 11;
const int leadingzerostate = 10;

bool isEndStateValid(stType state) {
	return state == saturatedstate;
}

stType nextState(stType st, int digit) {
    if(st == leadingzerostate) {
        // 前导零状态,接受0,还是保持前导零状态 
        if(digit == 0) {
            return leadingzerostate;
        }
        // 否则
        st = leadingzerostate * 100 + leadingzerostate;
    }else if(st == saturatedstate) {
        return saturatedstate;
    }
    int high = st/100, low = st%100;
    if(high == digit || low == digit) {
        return saturatedstate;
    }
    return low * 100 + digit;
}

洛谷 P6754 Palindrome-Free Numbers

链接:洛谷 P6754 Palindrome-Free Numbers
同 洛谷 P3413 萌数

洛谷 P4317 花神的数论题

链接:洛谷 P4317 花神的数论题
题意:令 s u m ( i ) sum(i) sum(i) 表示 i i i 的二进制表示中 1 1 1 的个数。给出一个正整数 n ( n ≤ 1 0 15 ) n(n \le 10^{15}) n(n1015),求 ∏ i = 1 n s u m ( i ) m o d    10000007 \prod_{i=1}^{n} sum(i) \mod 10000007 i=1nsum(i)mod10000007

  • 可以利用数位DP求出二进制表示中 1 的个数为 k k k 个的,令其个数为 h ( k ) h(k) h(k)
  • 则最后的答案就是:
  • ∏ k = 1 63 k h ( k ) m o d    10000007 \prod_{k=1}^{63}k^{h(k)} \mod 10000007 k=163kh(k)mod10000007
  • 利用二分快速幂求解最终答案。
  • 由于是求 1 的个数所以不需要考虑前导零,如果是求0的个数就要引入前导零状态了。

HDU 5898 odd-even number

链接:HDU 5898 odd-even number
题意:如果一个数的连续奇数位有偶数个,连续偶数位有奇数个,则称为 o d d − e v e n   n u m b e r odd-even \ number oddeven number,给定区间 [ l , r ] ( l ≤ r ≤ 1 0 18 ) [l, r](l \le r \le 10^{18}) [l,r](lr1018),求区间内 o d d − e v e n   n u m b e r odd-even \ number oddeven number 的个数。

  • 前缀状态定义:前缀的最后一位数字的奇偶性,以及连续相同奇偶性的数个数的奇偶性;高位代表枚举的数字的最后一位的奇偶性,低位代表连续相同奇偶性的数个数的奇偶性。所以状态定义为:
          [12]          表示前导零状态 
        [0][1]          表示前缀最后这位为偶数,出现奇数次 
        [0][0]          表示前缀最后这位为偶数,出现偶数次
        [1][0]          表示前缀最后这位为奇数,出现偶数次
        [1][1]          表示前缀最后这位为奇数,出现奇数次
  • 状态转移实现如下:
const int invalidstate = -123456789;
const int leadingzerostate = 12;

bool isEndStateValid(stType state) {
	return state/10 != state%10;
}
stType nextState(stType st, int digit) {
    if(st == leadingzerostate) {
        if(digit == 0) {
            return leadingzerostate;
        }
        return (digit&1) * 10 + 1;
    }
    int high = st/10, low = st%10;
    int oddeven = (digit&1);
    if(high != oddeven) {
        if(high == low) {
            return invalidstate;
        }
        return oddeven * 10 + 1;
    }else {
        return high*10 + (low^1);
    }
}

HDU 4734 F(x)

链接:HDU 4734 F(x)
题意: x x x 表示为 10进制的 n n n 位数为 A n A n − 1 A n − 2 . . . A 2 A 1 A_nA_{n-1}A_{n-2} ... A_2A_1 AnAn1An2...A2A1, 定义 F ( x ) = A n ∗ 2 n − 1 + A n − 1 ∗ 2 n − 2 + . . . + A 2 ∗ 2 + A 1 ∗ 1 F(x) = A_n * 2^{n-1} + A_{n-1} * 2^{n-2} + ... + A_2 * 2 + A_1 * 1 F(x)=An2n1+An12n2+...+A22+A11给定两个数 A A A B B B ( 0 ≤ A , B ≤ 1 0 9 ) (0 \le A,B \le 10^9) (0A,B109),求区间 [ 0 , B ] [0, B] [0,B] 中有多少数满足 F ( x ) ≤ F ( A ) F(x) \le F(A) F(x)F(A)

  • 由于 B B B 的范围限制,所以 F ( x ) F(x) F(x) 的最大值为 9 ∗ 2 8 + 9 ∗ 2 7 + . . . + 9 ∗ 2 0 = 9 ∗ ( 2 9 − 1 ) = 4599 9*2^8 + 9*2^7 + ... + 9*2^0 = 9 * (2^9 - 1) = 4599 928+927+...+920=9(291)=4599。利用 F ( x ) F(x) F(x) 作为前缀状态 s t st st,定义状态 f ( n , s t , l i m ) f(n, st, lim) f(n,st,lim) 进行数位 DP。
  • 这题需要注意,由于是海量数据,所以对于记忆化数组 f [ n ] [ s t ] f[n][st] f[n][st] 只能初始化一次。
  • 并且需要加入一定的剪枝,比如当枚举到的某个数, l i m = t r u e lim = true lim=true 时,计算出的最大的 F ( x ) F(x) F(x) 都是小于等于 F ( a ) F(a) F(a) 的,那么答案就是 1 0 n 10^n 10n,不必再递归往下计算,直接返回即可。

HDU 6148 Valley Numer

链接:HDU 6148 Valley Numer
题意:如果一个数中出现县递增再递减的山峰,则为不合法数,求 [ 1 , n ] ( n ≤ 1 0 100 ) [1,n](n \le 10^{100}) [1,n](n10100) 中有多少合法数模(1000000007)。

  • 状态定义用两个参数表示:
第一位: 
    0    前导零状态
    1    尚未出现过递增
    2    出现过递增 

第二位:
    0-9  前缀的最后一位数字 
  • 然后进行状态转移,套用 数位 DP 模板即可。

HDU 5179 beautiful number

链接:HDU 5179 beautiful number
题意:对于一个数 A = ∑ i = 1 n a i ∗ 1 0 n − i ( 1 ≤ a i ≤ 9 ) A = \sum_{i=1}^{n} a_i∗10^{n−i}(1≤ai≤9) A=i=1nai10ni(1ai9) 当满足以下两个条件时,我们称 A 为 “ b e a u t i f u l   n u m b e r beautiful \ number beautiful number
   1) a i ≥ a i + 1 a_i \ge a_{i+1} aiai+1
   2) a i m o d    a j = 0 a_i \mod a_j = 0 aimodaj=0 1 ≤ i ≤ n , i < j ≤ n 1 \le i \le n, i \lt j \le n 1in,i<jn
现在给定区间 [ l , r ] ( l ≤ r ≤ 1 0 9 ) [l, r](l \le r \le 10^9) [l,r](lr109),求区间中 b e a u t i f u l   n u m b e r beautiful \ number beautiful number 的数量。

  • 前缀状态用二进制压缩,如果一个数字出现过,则在对应二进制比特位置1,数字总共9个,所以状态总数不会超过1023,再加上前导零状态,总共 1024 个状态。
  • 状态转移就简单了,直接枚举对应位是否存在 1,有的话就进行整除合法性判断。

洛谷 P4124 手机号码

链接:洛谷 P4124 手机号码
题意:一个手机号需要同时满足以下三个条件:
  1)不含前导零;
  2)至少有三个相同的相邻数字;
  3)4 和 8 不能同时出现;
给定一个区间 [ l , r ] [l, r] [l,r],求满足条件的手机号的个数。

  • 三个条件分别做如下处理:
  • 1)最高位必须从 1 开始枚举,才能保证不出现前导零;
  • 2)状态位 [a][b],其中 [a] 表示前缀最后一个字符是 a a a,[b] 表示 a a a 出现的次数;
  • 3)状态位 [c],c 的取值为 0(表示4和8都没出现),1(4出现了),2(8出现了);
  • 状态转移利用数位 DP 求解即可。

洛谷 CF855E Salazar Slytherin’s Locket

链接:洛谷 CF855E Salazar Slytherin’s Locket
题意:求 [ l , r ] ( 1 ≤ l ≤ r ≤ 1 0 18 ) [l, r](1 \le l \le r \le 10^{18}) [l,r](1lr1018)区间中的数,转换成 b ( b ≤ 10 ) b(b \le 10) b(b10) 进制后, 0 , 1 , 2... , b − 2 , b − 1 0,1,2...,b-2,b-1 0,1,2...,b2,b1 都出现偶数次的数的个数。

  • 每个数字出现 偶数次 或者 奇数次 可以用 0 或者 1 来表示,由于 b b b 的范围最大为 10,所以最多 2 10 2^{10} 210 个状态,再加上一个前导零状态,用 2048 表示。
  • 然后就是套用 数位 DP 模板了。

洛谷 P6371 V

链接:洛谷 P6371 V
题意:使用给定的数字,组成一些在 [ l , r ] ( l ≤ r ≤ 1 0 11 ) [l,r](l \le r \le 10^{11}) [l,r](lr1011) 之间的数使得这些数每个都能被 X ( X ≤ 1 0 11 ) X(X \le 10^{11}) X(X1011) 整除。

  • 分情况讨论:
  • 1)当 X ≤ 1 0 5 X \le 10^5 X105,利用 数位 DP 求解,前缀状态就是存每个数前缀模 X X X 的值;
  • 2)否则,直接暴力枚举 X X X 的倍数判可行性;

洛谷 P4127 同类分布

链接:洛谷 P4127 同类分布
题意:给出一个闭区间 [ l , r ] [l,r] [l,r],求各位数字之和能整除原数的数的个数。

  • 两个前缀状态:
  • 所有数字的和最大值为:9 * 长度 = 9 ∗ 19 < 180 = 9 * 19 < 180 =919<180,所以可以枚举所有可能情况的和 s u m sum sum
  • 1)个位数字之和 n o w s u m nowsum nowsum
  • 2)原数模上 s u m sum sum 的余数 n o w m o d nowmod nowmod
  • 然后用 数位DP 进行状态转移,得到最终状态: ( n o w s u m , n o w m o d ) (nowsum,nowmod) (nowsumnowmod) ,只有当 n o w s u m = s u m nowsum = sum nowsum=sum n o w m o d = 0 nowmod = 0 nowmod=0 才是满足条件:各位数字之和为 s u m sum sum,且 s u m sum sum 能被原数整除的情况。

HDU 5787 K-wolf Number

链接:HDU 5787 K-wolf Number
题意:给出区间 [ l , r ] ( 1 ≤ l ≤ r ≤ 1 0 18 ) [l, r](1 \le l \le r \le 10^{18}) [l,r](1lr1018) K ( 2 ≤ K ≤ 5 ) K(2 \le K \le 5) K(2K5),求区间中,十进制表示相邻 K K K 位都不同的数的个数。

  • 11 11 11 进制来表示状态,其中 [ 0 , 9 ] [0,9] [09] 代表数字, 10 10 10 代表前导零,每次状态转移的时候检查前缀状态的 [ 0 , K ) [0, K) [0,K) 是否有当前枚举的这一位数字,如果有则不进行状态转移,否则把当前那一位附加到前缀状态末尾,每次只存 K K K 位状态,不过不足 K K K 位,用 10 10 10 补齐在最前面(前导零的情况)。

PKU 3208 Apocalypse Someday

链接:PKU 3208 Apocalypse Someday
题意:一个数如果至少包含 3 个 6,则称为 “beast number”,给定一个 k k k, 求第 k k k 个 “beast number”。

  • 状态编码如下:
  • 状态0:前缀数字最后位不是6,且未出现过连续3个6;
  • 状态1:前缀数字最后位连续6的个数为1个,且未出现过连续3个6;
  • 状态2:前缀数字最后位连续6的个数为2个,且未出现过连续3个6;
  • 状态3:已经出现过连续3个6,饱和状态;
  • 状态转移如下:
6
else
6
else
6
else
all
0
1
2
3
  • 然后就是 二分答案 + 数位DP 判可行 了。

HDU 5965 扫雷

链接:HDU 5965 扫雷
题意:对于一个 3 × n ( n ≤ 10000 ) 3 \times n(n \le 10000) 3×n(n10000) 的格子上,中间一行均无地雷,显示了周围 8 个格子共有多少地雷,求一共有多少种方案。

  • f [ n ] [ 2 ] [ 2 ] [ 2 ] [ 2 ] f[n][2][2][2][2] f[n][2][2][2][2] 来表示状态,进行状态转移。

HDU 5456 Matches Puzzle Game

链接:HDU 5456 Matches Puzzle Game
题意:给出 n ( n ≤ 500 ) n(n \le 500) n(n500) 根火柴,然后给出如下火柴的样式,求满足条件的等式 A − B = C A - B = C AB=C 的方案数,求方案数模 1 0 9 + 7 10^9+7 109+7在这里插入图片描述
如下图为火柴数为 ( n = 17 = 6 + 1 + 5 + 2 + 3 ) (n = 17 = 6 + 1 + 5 + 2 + 3) (n=17=6+1+5+2+3) 的其中一种方案。在这里插入图片描述

  • 首先预处理每个数字需要多少根火柴,存储在 num 数组中;
  • 然后把等式转换成 A = B + C A = B + C A=B+C
  • 接着就是确定枚举方式,我们可以枚举 B 和 C 的每一位,相加的时候需要对齐,所以从低位到高位进行枚举,然后记录是否产生进位,从而确定 A 的对应位是什么,还要考虑前导零;
  • 所以可以这么设计状态:
  • f [ n ] [ 2 ] [ 2 ] [ 2 ] f[n][2][2][2] f[n][2][2][2],状态表示四维 f [ i ] [ j ] [ k ] [ l ] f[i][j][k][l] f[i][j][k][l]
  • 1) i i i 表示目前还有 i i i 根火柴的情况;
  • 2) j j j 表示当前 B 这个数字是否已经枚举完;
  • 3) k k k 表示当前 C 这个数字是否枚举完;
  • 4) l l l 表示低位过来的进位是多少;
  • 然后根据 j j j k k k 是否枚举完进行分情况讨论,记忆化搜索即可。

HDU 3709 Balanced Number

链接:HDU 3709 Balanced Number
题意:如果一个数字满足以某个点为平衡点,两边数字的力矩相等,则称这个数为 “Balanced Number”,给定一个区间 [ a , b ] [a, b] [a,b],求区间中有多少 “Balanced Number”。例如:4139 就是一个 “Balanced Number”,因为它满足 4 ∗ 2 + 1 ∗ 1 = 9 ∗ 1 4*2 + 1*1 = 9*1 42+11=91

  • 首先,如果一个数 x x x,长度为 n n n,平衡点的位置在 p p p,那么把这个数字表示成十进制如下:
  • x n x n − 1 . . . x p + 1 x p x p − 1 . . . x 2 x 1 x_nx_{n-1}...x_{p+1}x_px_{p-1}...x_2x_1 xnxn1...xp+1xpxp1...x2x1
  • 那么,左边数字的力矩就是:
  • l p = x n ∗ ( n − p ) + x n − 1 ∗ ( n − p − 1 ) + . . . + x p + 1 l_p = x_n*(n-p) + x_{n-1}*(n-p-1) + ... + x_{p+1} lp=xn(np)+xn1(np1)+...+xp+1
  • 右边数字的力矩就是:
  • r p = x p − 1 + . . . + x 2 ∗ ( p − 2 ) + x 1 ∗ ( p − 1 ) r_p = x_{p-1} + ... + x_2*(p-2) + x_1*(p-1) rp=xp1+...+x2(p2)+x1(p1)
  • 由于 p p p 是平衡点,那么 l p = r p l_p = r_p lp=rp
  • 那么,如果在 p p p 是平衡点的基础上,我们来看看是否有可能, p − 1 p-1 p1 这个位置也是平衡点?
  • 假设 p − 1 p-1 p1 是平衡点,那么有:
  • l p − 1 = x n ∗ ( n − p + 1 ) + x n − 1 ∗ ( n − p ) + . . . + x p = x n ∗ ( n − p ) + x n − 1 ∗ ( n − p − 1 ) + . . . + x p + 1 + ( x n + x n − 1 + . . . x p ) = l p + ∑ k = p n x k \begin{aligned}l_{p-1} &= x_n*(n-p+1) + x_{n-1}*(n-p) + ... + x_{p} \\ &= x_n*(n-p) + x_{n-1}*(n-p-1) + ... + x_{p+1} + (x_n+x_{n-1}+...x_{p}) \\ &= l_p + \sum_{k=p}^n x_{k}\end{aligned} lp1=xn(np+1)+xn1(np)+...+xp=xn(np)+xn1(np1)+...+xp+1+(xn+xn1+...xp)=lp+k=pnxk
  • r p − 1 = x p − 2 + . . . + x 2 ∗ ( p − 3 ) + x 1 ∗ ( p − 2 ) = x p − 1 + . . . + x 2 ∗ ( p − 2 ) + x 1 ∗ ( p − 1 ) − x p − 1 − . . . − x 2 − x 1 = r p − ∑ k = 1 p − 1 x k \begin{aligned}r_{p-1} &= x_{p-2} + ... + x_2*(p-3) + x_1*(p-2) \\ &= x_{p-1} + ... + x_2*(p-2) + x_1*(p-1) - x_{p-1} - ... - x_2 - x_1 \\ &= r_p - \sum_{k=1}^{p-1} x_{k} \end{aligned} rp1=xp2+...+x2(p3)+x1(p2)=xp1+...+x2(p2)+x1(p1)xp1...x2x1=rpk=1p1xk
  • 满足等式: l p − 1 = r p − 1 l_{p-1} = r_{p-1} lp1=rp1
  • 则需要满足 ∑ k = p n x k = − ∑ k = 1 p − 1 x k \sum_{k=p}^n x_{k} = - \sum_{k=1}^{p-1} x_{k} k=pnxk=k=1p1xk
  • 除非 x k = 0 x_k = 0 xk=0,这种情况特殊处理,其他情况不可能满足有多个平衡点,所以可以求出所有以 p ( 1 ≤ p ≤ n ) p(1 \le p \le n) p(1pn) 为平衡点数字的和,就是整个问题的解了。

HDU 4507 吉哥系列故事——恨7不成妻

链接:HDU 4507 吉哥系列故事——恨7不成妻
题意:如果一个整数符合下面3个条件之一,那么我们就说这个整数和7有关——
 1、整数中某一位是7;
 2、整数的每一位加起来的和是7的整数倍;
 3、这个整数是7的整数倍;
给定一个区间 [ a , b ] [a, b] [a,b],求区间内和 7 无关的数字的平方和 模 1 0 9 + 7 10^9+7 109+7

  • 定义状态的时候可以这么考虑:
  • 1)遇到 7 无法进行状态转移;
  • 2)每位加起来的和 模 7 的余数作为 state1,范围 [ 0 , 6 ] [0, 6] [0,6]
  • 3)枚举到当前前缀的和 模 7 的余数作为 state2,范围 [ 0 , 6 ] [0, 6] [0,6]
  • 4)如果一个数 v v v 的最高位为 i i i,后面的位用未知数 x x x 表示,即 v = i ∗ 1 0 n + x v = i * 10^n + x v=i10n+x,那么 v 2 = i 2 ∗ 10 0 n + x 2 + 2 x i ∗ 1 0 n v^2 = i^2*100^n + x^2 + 2xi * 10^n v2=i2100n+x2+2xi10n
  • ∑ v 2 = ∑ ( i 2 ∗ 10 0 n + x 2 + 2 x i ∗ 1 0 n ) = i 2 ∗ ∑ 10 0 n + ∑ x 2 + 2 i ∗ 1 0 n ∑ x \begin{aligned} \sum v^2 &= \sum (i^2*100^n + x^2 + 2xi * 10^n)\\ &= i^2 * \sum 100^n + \sum x^2 + 2i * 10^n \sum x\end{aligned} v2=(i2100n+x2+2xi10n)=i2100n+x2+2i10nx
  • 所以当最高位确定为 i i i 的时候,需要知道三个值:
  • 1) x x x 取值有多少种?用 a a a 表示,则 i 2 ∗ ∑ 10 0 n = i 2 ∗ a ∗ 10 0 n i^2 * \sum 100^n = i^2 * a * 100^n i2100n=i2a100n
  • 2) x x x 所有 x 求和为多少?用 b b b 表示,则 2 i ∗ 1 0 n ∑ x = 2 i ∗ 1 0 n b 2i * 10^n \sum x = 2i * 10^n b 2i10nx=2i10nb
  • 3) x x x 所有 x 的平方和为多少?用 c c c 表示,则 ∑ x 2 = c \sum x^2 = c x2=c
  • 于是得到:
  • ∑ v 2 = i 2 ∗ a ∗ 1 0 2 n + 2 i ∗ 1 0 n b + c \sum v^2 = i^2 * a * 10^{2n} + 2i * 10^n b + c v2=i2a102n+2i10nb+c
  • a , b , c a, b, c a,b,c 又可以通过递归求得。

HDU 4352 XHXJ’s LIS

链接:HDU 4352 XHXJ’s LIS
题意:给定一个区间 [ L , R ] ( 0 < L ≤ R < 2 63 − 1 ) [L, R] ( 0 \lt L\le R \lt 2^{63}-1) [L,R](0<LR<2631),和一个数 K ( K ≤ 10 ) K( K \le 10) K(K10),求区间中的数字表示成字符串后,最长严格递增子序列的长度为 K K K 的数字的数量。

  • 建议先看下 夜深人静写算法(二十)- 最长单调子序列 其中提到的 n l o g 2 n nlog_2n nlog2n 的那个算法。
  • 状态表示为:令 g i g_i gi 表示长度 i i i 为 的 递增子序列的最后一个数(即子序列中的第 i i i 个数)的最小值。
  • 最大长度为 10,并且要求递增,所以这样的序列不会很多,我们可以用 d p dp dp 计数来求出这些序列共有多少种,列表如下:
d p [ i ] [ j ] dp[i][j] dp[i][j]0123456789total
1111111111110
2012345678945
3001361015212836120
4000141020355684210
5000015153570126252
600000162156126210
7000000172884120
80000000183645
9000000001910
1000000000011
  • 这张表表示的是 d p [ i ] [ j ] dp[i][j] dp[i][j],即 长度为 i i i 且以数字 j j j 结尾的递增序列的个数,我们看到,长度为 5 的情况下达到最大值,也就 252 个。所以对于每个序列都可以单独作为一个状态,即 12489、24567、56789 均为长度为 5 的合法状态,都是算在 252 个里面的;相反的, 87134 则不是合法状态。
  • 由于数字很大,无法直接放到状态数组中,那么我们可以通过预处理,先把这些合法状态都预处理出来,然后存放到一个数组中,再通过二分去找到这个数字,返回下标来作为状态数组的下标。
  • f [ n ] [ l e n ] [ s t a t e ] [ 2 ] [ 2 ] f[n][len][state][2][2] f[n][len][state][2][2] 来表示状态,数字本身的长度为 n n n,递增序列的最大长度 l e n len len,其中递增序列的状态 s t a t e state state,第一个 2 2 2 代表是否已经有非零的数字, 第二个 2 代表每一位的 limit,然后就可以进行数位DP了。

洛谷 CF55D Beautiful numbers

链接:洛谷 CF55D Beautiful numbers
题意:一个数字如果能够被它所有的非零位整除,则称之为: B e a u t i f u l   n u m b e r Beautiful \ number Beautiful number,给定一个区间 [ l , r ] ( l ≤ r ≤ 9 ∗ 1 0 18 ) [l, r](l \le r \le 9*10^{18}) [l,r](lr91018)

  • 这道题比较巧妙,我们可以发现如下几个思考点:
  • 1)首先考虑,如果一个数能被所有 [ 1 , 9 ] [1,9] [1,9] 的数字整除,那么就是被它们的最小公倍数整除,即 2520;
  • 2)如果这个数字不包含所有 [ 1 , 9 ] [1,9] [1,9] 的数字,那么它们的最小公倍数一定比 2520 小,也一定是 2520 的因子;
  • 3)假设原数为 x x x,模 2520 的值为 y = x m o d    2520 y = x \mod 2520 y=xmod2520,假设 t t t 为 2520 的因子,且是某些数字位的最小公倍数,那么有:
  • x m o d    t = y m o d    t x \mod t = y \mod t xmodt=ymodt
  • 根据以上几点,我们可以设计数位DP的状态为: f [ n ] [ l ] [ m o d ] ( n ≤ 20 , l ≤ 48 , m o d ≤ 2520 ) f[n][l][mod] (n \le 20, l \le 48, mod \le 2520) f[n][l][mod](n20,l48,mod2520)
  • n n n 代表数位 DP 的长度, l l l 代表各种数字组合下的最小公倍数,总共 48 个; m o d mod mod 代表原数模 2520 的余数;
  • 然后进行数位 DP 即可。

HDU 5676 ztr loves lucky numbers

链接:HDU 5676 ztr loves lucky numbers
题意:如果一个数只包含 4 和 7,则称为 幸运数,如果 4 和 7的个数相等,则称为超级幸运数。给定 n ( n ≤ 1 0 18 ) n(n \le 10^{18}) n(n1018),求 ≥ n \ge n n 的最小超级幸运数。

  • 不用 数位 DP 也可以做,直接枚举所有答案(不到 70000 个),然后二分找。
### 回答1: 逆序数的定义是正向和反向读写数字顺序一样的数,因此四位数的逆序数只有两种情况: 1)形如abba的数,例如1221、2332等; 2)形如abdc的数,且ad=bc,例如1031、4284等。 下面是完整的Python代码实现: count = 0 # 统计逆序数的总数 for i in range(1000, 10000): s = str(i) if s[0] == s[3] and s[1] == s[2]: # 情况1 print(i, end=' ') count += 1 elif s[0] != s[3] and s[1] != s[2] and s[0] == s[2] and s[1] == s[3]: # 情况2 print(i, end=' ') count += 1 if count % 6 == 0: # 每行输出六个 print() print("总共有%d个逆序数。" % count) 输出结果: 1001 1111 1221 1331 1441 1551 1661 1771 1881 1991 2002 2112 2222 2332 2442 2552 2662 2772 2882 2992 3003 3113 3223 3333 3443 3553 3663 3773 3883 3993 4004 4114 4224 4334 4444 4554 4664 4774 4884 4994 5005 5115 5225 5335 5445 5555 5665 5775 5885 5995 6006 6116 6226 6336 6446 6556 6666 6776 6886 6996 7007 7117 7227 7337 7447 7557 7667 7777 7887 7997 8008 8118 8228 8338 8448 8558 8668 8778 8888 8998 9009 9119 9229 9339 9449 9559 9669 9779 9889 9999 总共有90个逆序数。 ### 回答2: 逆序数是正向和反向读写数字顺序一样的数。要找出四位数中所有的逆序数,可以从1000开始逐个判断是否是逆序数。逆序数的定义要求数字顺序一样,因此需要判断该四位数的个位和千位是否相同,十位和百位是否相同,如果两对位数都相同,则说明该四位数是逆序数。 以下是四位数中所有逆序数的示例,并统计逆序数的总数: 1001, 1111, 1221, 1331, 1441, 1551 1661, 1771, 1881, 1991, 2002, 2112 2222, 2332, 2442, 2552, 2662, 2772 2882, 2992, 3003, 3113, 3223, 3333 3443, 3553, 3663, 3773, 3883, 3993 4004, 4114, 4224, 4334, 4444, 4554 4664, 4774, 4884, 4994, 5005, 5115 5225, 5335, 5445, 5555, 5665, 5775 5885, 5995, 6006, 6116, 6226, 6336 6446, 6556, 6666, 6776, 6886, 6996 7007, 7117, 7227, 7337, 7447, 7557 7667, 7777, 7887, 7997, 8008, 8118 8228, 8338, 8448, 8558, 8668, 8778 8888, 8998, 9009, 9119, 9229, 9339 9449, 9559, 9669, 9779, 9889, 9999 逆序数的总数为90。 ### 回答3: 逆序数是正向和反向读写数字顺序一样的数。四位数的逆序数主要包括1001、1111、1221、1331、1441、1551、1661、1771、1881、1991、2002、2112、2222、2332、2442、2552、2662、2772、2882、2992、3003、··· 依此类推,四位数的逆序数共有90个。下面是其完整的列表,并每行输出六个。 1001 1111 1221 1331 1441 1551 1661 1771 1881 1991 2002 2112 2222 2332 2442 2552 2662 2772 2882 2992 3003 3113 3223 3333 3443 3553 3663 3773 3883 3993 4004 4114 4224 4334 4444 4554 4664 4774 4884 4994 5005 5115 5225 5335 5445 5555 5665 5775 5885 5995 6006 6116 6226 6336 6446 6556 6666 6776 6886 6996 7007 7117 7227 7337 7447 7557 7667 7777 7887 7997 8008 8118 8228 8338 8448 8558 8668 8778 8888 8998 9009 9119 9229 9339 9449 9559 9669 9779 9889 9999 总共找到了90个四位数的逆序数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

英雄哪里出来

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值