算法-数位dp
前置知识: dp \texttt{dp} dp, Dfs \texttt{Dfs} Dfs。
参考文献
https://www.cnblogs.com/y2823774827y/p/10301145.html
https://www.luogu.com.cn/blog/mak2333/solution-p2602
Introduction \texttt{Introduction} Introduction
数位 dp \texttt{dp} dp 是指求在数位限制下有多少满足要求的数的 dp \texttt{dp} dp。例如,求“在 [ L , R ] [L,R] [L,R] 范围内连续出现过 3 3 3 个 3 3 3 的数”,“相邻两位之间差为质数的 5 5 5 位数”或“在 [ L , R ] [L,R] [L,R] 区间内 6 6 6 出现的次数”。读完这篇文章以后,你就都会做了。
数位 dp \texttt{dp} dp 有两种主要方法: 循环递推 \color{6699e9}\texttt{循环递推} 循环递推或 记忆化搜索 \color{77c650}\texttt{记忆化搜索} 记忆化搜索。
先讲 循环递推 \color{6699e9}\texttt{循环递推} 循环递推,例题是数字计数。
Description \color{6699e9}\texttt{Description} Description
[ZJOI2010]数字计数
求在 [ a , b ] [a,b] [a,b] 区间内的数 0 ∼ 9 0\sim9 0∼9 数字分别出现次数,前导 0 0 0 不算。
数据范围: 1 ≤ a ≤ b ≤ 12 1\le a\le b\le 12 1≤a≤b≤12。
Solution \color{6699e9}\texttt{Solution} Solution
为了讲得更透彻,蒟蒻会把同一个东西用不同的方法多次描述,文章较长,请见谅。
Step 1 预处理
设 s u m i , j ( 1 ≤ i ≤ 12 , 1 ≤ j ≤ 9 ) sum_{i,j}(1\le i\le 12,1\le j\le 9) sumi,j(1≤i≤12,1≤j≤9) 表示数字 j j j 在满 i i i 位整数( [ 1 , 1 0 i − 1 ] [1,10^i-1] [1,10i−1])中出现的次数。因为除了 0 0 0 以外, 1 ∼ 9 1\sim 9 1∼9 在这题中其实是一模一样的,所以 s u m i , 1 = s u m i , 2 = . . . = s u m i , 9 sum_{i,1}=sum_{i,2}=...=sum_{i,9} sumi,1=sumi,2=...=sumi,9。
所以蒟蒻们还不如直接用 s u m i sum_i sumi 表示 s u m i , j sum_{i,j} sumi,j,表示数字 1 ∼ 9 1\sim9 1∼9 在满 i i i 位整数中出现的次数。所以 s u m 1 = 1 sum_1=1 sum1=1,因为 s u m 2 sum_2 sum2 可以由 s u m 1 sum_1 sum1 个数前面加 0 ∼ 9 0\sim 9 0∼9 递推得,也可以把数放在首位,所以 s u m n = 10 s u m n − 1 + 1 0 n − 1 ( 2 ≤ n ) sum_n=10sum_{n-1}+10^{n-1}(2\le n) sumn=10sumn−1+10n−1(2≤n)。 s u m sum sum 数列打表出来就是 1 , 20 , 300 , 4000 , . . . 1,20,300,4000,... 1,20,300,4000,...。
code
void pro(){
//其实代码很短
ten[0]=1;//10^0=1
for(int i=1;i<=12;i++){
ten[i]=ten[i-1]*10;
sum[i]=sum[i-1]*10+ten[i-1];
}
}
Step 2 DP
预处理完 s u m i sum_i sumi 后,可以抓一只 p p p 位数 n n n 求 0 ∼ 9 0\sim9 0∼9 在 [ 1 , n ] [1,n] [1,n] 中出现的次数。首先设 n l i ( 1 ≤ i ≤ p ) nl_i(1\le i\le p) nli(1≤i≤p) 表示 n n n 的从右往左第 i i i 位的数字。即 n l i = ⌊ n m o d 1 0 i 1 0 i − 1 ⌋ nl_i=\lfloor\frac{n \mod 10^i}{10^{i-1}}\rfloor nli=⌊10i−1nmod10i⌋。
code
int p; lng bit=n;
for(p=0;n;n/=10) nl[++p]=n%10;//最后p就是n的位数
然后令 f j