博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
P2602 [ZJOI2010]数字计数 (数位dp or 思维)
阅读量:4873 次
发布时间:2019-06-11

本文共 2239 字,大约阅读时间需要 7 分钟。

题目描述

给定两个正整数a和b,求在[a,b]中的所有整数中,每个数码(digit)各出现了多少次。

输入输出格式

输入格式:

输入文件中仅包含一行两个整数a、b,含义如上所述。

输出格式:

输出文件中包含一行10个整数,分别表示0-9在[a,b]中出现了多少次。

输入输出样例

输入样例#1:
1 99
输出样例#1:
9 20 20 20 20 20 20 20 20 20

说明

30%的数据中,a<=b<=10^6;

100%的数据中,a<=b<=10^12。






历时五个小数,终于知道题解在写什么了,泪目

找了很多这个题的题解,都是马马虎虎,完全不是给我这种蒟蒻看的好吗?qwq

 

抄的题解::::::

 

那么我们首先看题,对于这道题我一开始以为很水,但是当我仔细去读题之后发现事情没那么简单。其中对于这道题(递推做法)最大的难点是难以找出递推式子(废话,你写递推就只有这点难了)。为啥,因为你很难想到怎样求出第几位它的数字又多少,因为不能有前导0。但是我们发现如果不考虑是否有前导0的话,那么这道题就似乎有递推公式。

f[i]代表在有i位数字的情况下,每个数字有多少个。如果不考虑前导0,你会发现对于每一个数,它的数量都是相等的,也就是f[i]=f[i-1]*10+10^(i-1);(这里我推荐使用打表+大眼观察法)

然而这个公式推出来后,你就会面临第二个难题,怎么推出我想要的答案?

我们先设数字为ABCD

看A000,如果我们要求出它所有数位之和,我们会怎么求?

鉴于我们其实已经求出了0~9,0~99,0~999。。。上所有数字个数(f[i],且没有考虑前导0)我们何不把这个A000看成0000~1000~2000...A000对于不考虑首位每一个式子的数字的出现个数为 A*f[3]。加上首位出现也就是小于A每一个数都出现了10^3次,再加上,我们就把A000处理完了。

这样你以为就把第一位处理完了?不不不,首位A还出现了BCD+1次呢,也就是从A000~ABCD,这个A还出现了BCD+1次,所以再加上这些才行呢。那么你发现,我们成功把首位代表的所有数字个数求出来了,剩下的求解与A完全没有任何关系,只是BCD的求解,于是我们发现我们已经把一个大问题,化成了一个个小问题,也即是,对于一个这样n位的数,把他一位位的分离开来。

当然你还需要处理前导0你会发现前导0一定是0001,0002。。。0012,0013。。。0101,0102.。。0999这样的数,一共出现了10*(i-1)+10*(i-2)+...10 (i表示数字位数),让0的统计减去这个值,那么恭喜你这道题做完了。

总结 对于DP这个东西,最重要的其实只有一点,推状态,状态又是什么?是大问题的子问题,对于这种题最重要的特点是,无后效性,问题可拆分,并且答案的求解具有一定的规律,这样的题应该就可以用DP做,数位DP最重要的就是把一整个数字拆分成一位一位的单独来看,那么对于数位DP,它的子问题也就一般是每一位上对于答案的求解,层层递进的这么一个思路。

 

从最高位开始,是为了在最高位是最大数基础上在进行数字的统计!







 

1 #include
2 using namespace std; 3 long long a,b; 4 long long ten[20],f[20]; 5 long long cnta[20],cntb[20]; 6 void solve(long long x,long long *cnt) 7 { 8 long long num[20]={
0}; 9 int len=0;10 while(x)11 {12 num[++len]=x%10;13 x=x/10;14 } 15 for(int i=len;i>=1;i--)16 {17 for(int j=0;j<=9;j++)18 cnt[j]+=f[i-1]*num[i];19 for(int j=0;j
=1;j--)23 {24 num2=num2*10+num[j];25 }26 cnt[num[i]]+=num2+1;27 cnt[0]-=ten[i-1];28 } 29 }30 int main()31 {32 scanf("%lld %lld",&a,&b);33 ten[0]=1;34 for(int i=1;i<=15;i++)35 {36 f[i]=f[i-1]*10+ten[i-1];37 ten[i]=10*ten[i-1];38 }39 solve(a-1,cnta);40 solve(b,cntb);41 for(int i=0;i<=9;i++)42 printf("%lld ",cntb[i]-cnta[i]);43 }

 

 

 

 

 

 

 

 

 

 

 

 

 

转载于:https://www.cnblogs.com/zhangbuang/p/10555068.html

你可能感兴趣的文章
Android安全-代码安全1-ProGuard混淆处理
查看>>
部署core
查看>>
mysql 时间设置
查看>>
如何在 Xcode 中修改应用的名字
查看>>
[BZOJ5334][TJOI2018]数学计算(exgcd/线段树)
查看>>
[BZOJ4340][BJOI2015]隐身术(后缀数组)
查看>>
有关交换机——熟悉原理是必须的【转载】
查看>>
ACM(数学问题)——UVa202:输入整数a和b(0≤a≤3000,1≤b≤3000),输出a/b的循环小数表示以及循环节长度。...
查看>>
【转】Android 读取doc文件
查看>>
P2602 [ZJOI2010]数字计数 (数位dp or 思维)
查看>>
C# 访问 C DLL
查看>>
Matlab与方程组
查看>>
NYOJ 477
查看>>
华为、科达、海康、大华等厂家摄像头通过非标方式(RTSP)接入流媒体服务实现WEB直播与录像...
查看>>
OSPF笔记
查看>>
PHP之abstract
查看>>
Rappid 消除试用版的弹出框
查看>>
精华 ionic入门之色彩、图标、边距和界面组件:列表
查看>>
顺变者昌
查看>>
Linux上vi(vim)编辑器使用教程
查看>>