数位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的出现次数即可