数位DP
P2602 数字计数
给定正整数a,b,求$[a,b]$所有的整数中,0到9每个数码各自总共出现多少次
由前缀和的思想,设$sum[n]$表示$[0,n]$中0到9各个数码各自出现了多少次
那么$sum[b]-sum[a-1]$就是所求值
问题转化为如何求$sum[n]$即$[0,n]$中0到9各个数码各自出现了多少次
设计状态$dp[i]$表示”当范围涵盖了完整的i位时,前i位中每个数字出现的次数.”
啥意思呢?
比如对于$[1000,1999]$显然能够找出i=1的完整区间,比如$[1000,1009]$等等
显然也能够找出i=2的完整区间,比如$[1900,1999]$等等
显然也能够找出i=3的完整区间,比如$[1000,1999]$,只有这一个
找不出i=4的完整区间了
如果对于[10,2000000],这就可以找到i=4的完整区间,也就是**低四位能够遍历[0,9999]**这个范围的区间,比如
$[10000,19999]$
这样设计dp有一个隐患就是,忽略了前导0的区别.比如给定区间[0,999],显然最大可以找到i=3的区间[0,999]
但是正常情况下,0不能作为前导,也就是说不能有012或001或000或020这种数字,可以有210或100这种数字
最后再考虑去除前导零的情况,现在先不考虑
状态转移方程为:
$$
dp[i]=10\times dp[i-1]+10^{i-1}
$$
如何考虑这个方程呢?排列组合问题
第i位假设放了一个X(先不管到底放了啥),考虑他对低i-1位的影响,
X有0,1,2,…,9十种可能,于是之前计算得到的dp[i-1]就得乘以10,
也就是说,1在低i-1上出现过$10\times dp[i-1]$次
okk,现在看低i-1位给第i位造成的影响,第i位假如放一个1,低i-1位随便放有$10^{i-1}$种放法
因此1就在第i位上出现过$10^{i-1}$次
举个实际例子,以[0,999]为例,前导零的情况也计算在内
$dp[1]=1$,边界条件,意思是区间为[0,9]的时候,一个数字显然只出现一次
$dp[2]=10\times dp[1]+10^{1}=20$
假设个位上放一个X,十位有十种可能,0X,1X,2X,3X,…,9X,也就是说,十位对个位计数的贡献就是$10\times dp[1]$
假设十位上放一个X,个位有十种可能,X0,X1,X2,X3,…,X9.也就是说,个位对十位计数的贡献就是$10^{1}$
笑死,只有两位,要不你影响我,要不我影响你,怎么贡献方式还不一样?
再多一位就可以体现了
$dp[3]=10\times dp[2]+10^{2}=300$
这时候发现貌似不容易计算百位对于前两位的贡献了,如何解释$10\times dp[2]$怎么来的呢?
可以这样想:
原来在计算$dp[2]$时,这样只有一个[0,99]区间,而考虑了百位之后,就有
$[0,99],[100,199],[200,299],…,[900,999]$这10个区间了,每个区间内数字的出现个数都是$dp[2]$现在有十个相同区间,因此当有三位的时候,从前两位计算得到的数字出现个数就是$10\times dp[2]$
假设百位上放一个X,前两位稍微一动就是一个新数,X就得多记录一次
比如X23,X24,X25,这样百位上的X就记了3次.那么前两位一共有多少种可能的排列呢?$10^2$那么X在第三位上出现的次数就是$10^2$
然后考虑如何去除前导0
去除前导零,也就是给0的计数中去掉0出现在最高位的情况
去除前导0,只会对0的计数有影响,对其他数字没有影响,因为
012345去除前导0不是说把012345给去掉,而是保留12345,12345这五个数字各自统计一次,0此时不应计数
要去除i=6上的前导零计数,也就是去除
0XXXXX这种情况对0计数的贡献,显然有$10^5$种
去除i=5上的前导零计数,也就是去除
0XXXX这种情况对0计数的贡献,显然有$10^4$种
也就是去除第i位上的前导0影响,就是去除$10^{i-1}$次0的计数
1 |
|
其中比较抽象的是后面这个循环,他里面都干了什么呢?
1 | for(auto j=0;j<10;++j)result[j]+=dp[i-1]*top[i]; |
计算当前位i随便安排时,对前i位中,数位统计的贡献
使用$top[i]$因为第i位上只有$[0,top[i]-1]$共$top[i]$种情况
为什么不是$[0,top[i]]$共$top[i]+1$种情况?假如对于$[0,6524]$
当前i=4,top[4]=6,可以找到区间[0,999],[1000,1999]
[2000,2999],[3000,3999],[4000,4999],[5000,5999]
共6个=top[4]
没有包括[6000,6999],因为最大到6524,即top[i]作为最高位时前i位找不出完整的区间来
1 | for(auto j=0;j<top[i];++j)result[j]+=pow(10,i-1); |
计算前i位随便安排时,对第i位数位统计的贡献
由于最高位上只可能出现$[0,top[i]-1]$,再高top[i]没有完全区间,再高$top[i]+1$不可能出现,因此不能统计对$[top[i],9]$这些数字的贡献
比如当n=6524
i=6,也就是说存在1XXX,2XXX,3XXX,4XXX,5XXX这些完整区间(XXX表示[0,999])
6XXX没有完全区间,只有[6000,6524]
更不存在7XXX了
1 |
|
啥意思呢?还是n=6524举例子
temp=n=6524
然后temp=6524-6000=524
这524就是对top[4]=6统计次数的贡献,也就是6在i=4位上出现了524+1=525次
加一的原因是,524表示[0,524]共525种情况
1 | result[0]-=pow(10,i-1);//去除前导零 |
前导零有多种情况,比如0524,0024,0004,0000
这里每次只会处理一种情况,i=4时只会消除0999,0998,…,0000这种前导0.
P8764 二进位问题
$[1,n]$这些整数中,二进制表示有k个1的有多少个
如果问题变成:m个二进制位,其中有k个1的有多少个数
显然是$C_m^k$
假设n表示成二进制形式之后,最高位是$bit[len]=1$,最低位是$bit[1]$
显然前$len-1$位所有状态都包含在内了,因此首先有$C_{len-1}^k$,也就是最高位位0时,低$len-1$位任意安排
下面考虑最高位为1的情况,
从次高位$bit[len-1]$往低位bit[1]方向看
如果$bit[i]$为0则跳过,否则加上$C_{i-1}^{k-t}$
其中t表示$[i+1,len]$这些位上的1个数
比如$n=101000,len=6$
i | bit[i] | 操作 |
---|---|---|
5 | 0 | 跳过 |
4 | 1 | $+C_{4-1}^{k-1}$ |
为啥加上这个数呢?第4位上有一个1,这表明存在
$[100000b,100111b]$,低三位存在完整区间,从这个完整区间中统计有$k-1$个1的数,是因为之前已经有一个1了,需要在剩下的位中找出k-1个1即可
现在的主要问题是如何快速求解$C_m^k$
$C_m^0=1$
$C_m^k=C_{m-1}^{k-1}+C_{m-1}^k$
用$c[m][k]$数组预处理即可
1 |
|
P2657 windy数
定义windy数:相邻两个十进制位必须至少差2,比如135,2597等等
给定区间[l,r]求这之间的windy数有多少
问题转化为[1,n]中的windy数有多少
设计状态:
$$
dp[i][j]表示i个10进制位,最高位第i位上是j的windy数的个数
$$
边界条件显然是只有一位的时候,$dp[1][j]=1$
转移方程:
$$
dp[i][j]=\sum_{|k-j|\ge2}dp[i-1][k]
$$
下面考虑[1,n]中有多少windy数
首先划分成十进位
dec[len],dec[len-1],…,dec[1],最高位是dec[len]
那么显然有低len-1位的完整区间,全算上
第len位上,从1到dec[len]-1这些dp[len][i]
也是都有的
只剩下len位上放dec[len]的情况了,这是最贴近上边界的情况,如何考虑?
i从len-1往1遍历
对于当前位i,尝试放置$0\le j<dec[i]$,因为第i位最高可以为dec[i],比他小的数都存在
然后判断j是否合法,依据就是j和dec[i+1]距离是否大于等于2,
如果是,则加上$dp[i][j]$
这样第i位就统计完毕,尝试往更低位统计,第i位最终放置的是dec[i]
如果dec[i+1]和dec[i]相差小于2,就不能继续往低位统计了
1 |
|
P4999 烦人的数学作业
给定区间[l,r],求每个数各个十进制位的数字和
比如[123,125],就有1+2+3+1+2+4+1+2+5
首先问题转化为[1,n]区间上拆分数字和P1836 数页码
然后问题还可以转化为P2602求每个数字出现的次数
最终累计i×i的出现次数即可