[BZOJ 1833] count 数字计数 数位DP(附数位DP总结)
来源:互联网 发布:李俊峰的霍去病 知乎 编辑:程序博客网 时间:2024/06/01 09:00
题目传送门:【BZOJ 1833】
题目大意:给定两个正整数 a 和 b,求在 [a,b] 中的所有整数中,每个数码 (digit,指 0-9 ) 各出现了多少次。其中 a ≤ b ≤ 10
题目分析:
真正的题解在下面,前面为总结。
由题,观察这道题的题目性质及数据范围,那么,这道题用线性的 for 一遍肯定是不行的了;于是,我们考虑缩小数据处理范围。又因为这道题满足“前缀和相减”的性质,所以我们考虑使用数位 DP。
那么确定了方法之后,我们先要了解数位 DP 的做法。它通常是遍历每一位数,对每一位数及其之后的数统计答案。
以下的“第几位”指从低到高的位数。
实际上,数位 DP 有一个大体模型:
对这个数进行 dfs,通常,dfs 有两个必要的参数 dep(th) 和 limit;dep 表示现在遍历到第几位了,而 limit 表示这一位是否达到需要被处理到的极限。
- int dfs(int dep,bool limit, … ){
- if (dep==0){
- return 1;
- }
- if (!limit){
- 处理每个ans;
- return ans;
- }
- int cnt=0,tot=0;
- int lim=(limit==true ? wei[dep] : 9);
- for (int i=0;i<=lim;i++){
- cnt=0;
- cnt+=dfs(dep-1,limit && i==lim,lead && i==0);
- 处理每个cnt;
- tot+=cnt;
- }
- return tot;
- }
假设现在没有其他的附加条件。
例如,对于一个数 3456,当最高位(千位)为 0,1,2 时,后面几位可以随意取值(0-9皆可);而当千位为 3 时,百位只能取到 0-4,所以这时我们要进入下一层查找。
当某一位取了一个值之后,接下来的几位都可以随意取值时,我们就可以将“可以随意取值”这个状态记录下来,这时我们就可以直接返回,避免浪费过多时间;所以,我们仅在无法随意取值的时候才进行递归查找操作。(当然也可以先预处理“可以随意取值”的状态)
而如果题目中有其他条件时(例如不能连续两位都是 1),我们可以增加一些新的参数;例如 prev 表示上一位取的数是多少,这时对于这个问题而言,如果上一个取了 1,那么这一位再取 1 将不是合法状态,所以此时我们就要把选 1 的情况排除掉(for 里面用一个 continue)。
那么对于这道题而言,它要求我们把每个数的出现次数统计出来,所以我们需要开两个桶,对于 [a,b],一个用来装 [1,a] 的答案,另一个用来装 [1,b] 的答案。同时,我们在 dfs 里面新增一个参数 lead,表示它是否为“存在前导 0 ”的数。
设目前的这个数的位数为
如果 dfs 在当前位 dep 含有前导 0,对于“0”这个数,我们只统计
下面附上代码:
- #include<cstdio>
- #include<cstring>
- #include<iostream>
- #include<algorithm>
- using namespace std;
- typedef long long LL;
- const int MX=13;
- const int INF=0x3f3f3f3f;
- LL f[MX],g[MX]; //f表示无前导0时,0-9的总方案数;
- //g表示有前导0时,0这个数的总方案数
- int wei[MX],top; //wei:每一位数的值
- //top:总位数
- LL bucket[10],temp[10]; //bucket:桶,存下0-9每一个数出现的次数
- //temp: 临时的桶,开两个桶方便最后计算
- LL fastpow(LL a,LL b){ //快速幂
- LL r=1,base=a;
- while (b){
- if (b&1) r*=base;
- base*=base;
- b>>=1;
- }
- return r;
- }
- LL dfs(int dep,bool limit,bool lead){ //对每一位进行处理
- //dep:当前位 limit:是否达到这道题的限制
- //lead:是否有前导0
- if (dep==0){
- if (!lead) return 1;
- else return 0;
- }
- if (!limit){
- if (!lead) bucket[0]+=f[dep];
- else bucket[0]+=g[dep];
- for (int i=1;i<=9;i++){
- bucket[i]+=f[dep];
- }
- return fastpow(10,dep)*(!lead);
- }
- LL cnt=0,tot=0; //cnt:这一位的计数
- //tot:之后所有位的计数
- int lim=(limit==true ? wei[dep] : 9);
- for (int i=0;i<=lim;i++){
- cnt=0;
- cnt+=dfs(dep-1,limit && i==lim,lead && i==0);
- bucket[i]+=cnt;
- tot+=cnt;
- }
- return tot;
- }
- int main(){
- LL a,b;
- scanf(”%I64d%I64d”,&a,&b);
- f[0]=f[1]=1;
- for (int i=2;i<=12;i++){
- f[i]=10*f[i-1]+fastpow(10,i-1);
- g[i]=9*f[i-1]+g[i-1];
- }
- LL tempa=a-1,tempb=b; //根据前缀和计算
- int w=0;
- while (tempa){
- wei[++w]=tempa%10;
- tempa/=10;
- }
- top=w;
- dfs(top,true,true);
- for (int i=0;i<=9;i++){
- temp[i]=bucket[i];
- bucket[i]=0;
- }
- w=0;memset(wei,0,sizeof(wei));
- while (tempb) {
- wei[++w]=tempb%10;
- tempb/=10;
- }
- top=w;
- dfs(top,true,true);
- for (int i=0;i<=9;i++){ //统计答案
- printf(”%I64d”,bucket[i]-temp[i]);
- if (i!=9) printf(“ ”);
- }
- return 0;
- }
- [BZOJ 1833] count 数字计数 数位DP(附数位DP总结)
- bzoj 1833 count 数字计数 数位dp
- BZOJ 1833 count 数字计数 数位DP
- bzoj 1833: [ZJOI2010]count 数字计数(数位dp)
- BZOJ 1833 count 数字计数 (数位DP)
- BZOJ 1833 ZJOI2010 count 数字计数 数位DP
- BZOJ 1833 ZJOI 2010 count 数字计数 数位DP
- [BZOJ 1833][ZJOI 2010]count数字计数(数位DP)
- BZOJ 1833 [ZJOI2010]count 数字计数(数位dp)
- 【BZOJ 1833】 [ZJOI2010]count 数字计数|数位DP
- [bzoj 1833] [ZJOI2010]count 数字计数:数位DP
- 【BZOJ 1833】【ZJOI 2010】[数位DP]count 数字计数
- BZOJ-1833 [ZJOI2010]count 数字计数 数位DP
- bzoj 1833 [ZJOI2010]count 数字计数 数位dp
- 1833: [ZJOI2010]count 数字计数 数位dp
- count 数字计数 (数位dp)
- bzoj 1833 数字计数|数位dp
- 【bzoj 1833】【codevs 1359】 [ZJOI2010]count 数字计数(数位dp)
- 各种注解的使用
- Python assert
- HTML5-拖放
- Ubuntu 14.04 更换源
- 小练手理工大学官网HTML+CSS
- [BZOJ 1833] count 数字计数 数位DP(附数位DP总结)
- iscroll5
- 可以回跳的台阶跳
- JSON
- HTML5-web 存储
- 用OpenCV的distanceTransform函数作图像的距离变换
- JAVA培训学习笔记——CSS3选择器
- MVVM上下文DataContent弹窗传参失效问题,ViewModelLocator找不到传参数据
- Jquery 操作 Html Table 实例