好久没写博客了,因为感觉时间比较紧,另一方面没有心思,做的题目比较浅也是另一方面。

热身赛第二场被血虐了好不好,于是决定看看数位DP吧。

进入正题:

如题是一道经(简)典(单)的数位dp。

第一步,对于数K^n-1这种形式的数,位数为n,它的各个位上,每个数0~K-1出现过的次数是一样的。

于是对于数B=K^n-1,有f(B)=(B+1)*n*(0+1+2+...+K-1)/K=(B+1)*n*(K-1)/2;

程序为:

  1. LL sum1(int pre,int n,int k)
  2. {
  3. LL ret=;
  4. LL pw=;
  5. for(int i=;i<n;i++) pw*=k;
  6. ret=pre*pw+pw*n*(k-)/;
  7. return ret;
  8. }

其中pre在这种情况下为0,pre是什么?我们立刻进入下一步讨论。

第二步,由第一步的结论,我们可以引申一下。为了更形象一点,我们不妨在十进制的情况下讨论。

现在我提出一个问题:如何计算0~49999的数它们各个位上数字之和?(K=10的前提下)

我们根据第一步可以很容易求出[0,9999]=(9999+1)*4*(10-1)/2。

那么还剩下[10000,19999],[20000,29999],[30000,39999],[40000,49999]该怎么求?

仔细观察发现[10000,19999]不过是每个数都比[0,9999]多了一个为1的万位,[20000,29999]不过是每个数都比[0,9999]多了一个为2的万位,[30000,39999]不过是每个数都比[0,9999]多了一个为3的万位,依次类推...就发现了规律。

所以此时这个与后面的数位都无关的万位,我们用i表示,万位之前没有其他的位,所以pre=0(如果对pre有点不理解,看完第三步就知道了),于是对于[i0000,i9999]这样的解就是((pre+i)*10000)+(9999+1)*4*(10-1)/2。

那么,不难得知,求解通式即为((pre+i)*K^n)+(K^n)*n*(K-1)/2。

第三步,基于第一步和第二步的结论,已经可以求出类似于999(K=10),39999(K=10),49999(K=10)的解。

现在又提出一个问题,对于[0,54321]我们怎么解?

当然,先延续之前“区间划分”+“前缀”的思路,先划分为[0,9999],[10000,19999],[20000,29999],[30000,39999],[40000,49999],[50000,54321]。

对于[0,9999],[10000,19999],[20000,29999],[30000,39999],[40000,49999]已经讨论过了,接下来讨论如何求[50000,54321]。

这时把万位的5看作一个前缀,区间就变为了[0,4321],于是只要求前缀pre=5的[0,4321]的解,也就是递归调用第二步的方法,这样就可以求到[0,321],[0,21],[0,1]这样把所有的解相加,就是需要的答案了。

  1. LL sum2(int pre,LL n,int k)
  2. {
  3. if(n<k){
  4. LL ret=;
  5. for(int i=;i<=n;i++) ret+=pre+i;
  6. return ret;
  7. }
  8. LL tn=n,pw=,ret=;
  9. int mi=;
  10. while(tn>=k){
  11. pw*=k;
  12. mi++;
  13. tn/=k;
  14. }
  15. for(int i=;i<tn;i++)
  16. ret+=sum1(pre+i,mi,k);
  17. ret+=sum2(pre+tn,n-tn*pw,k);
  18. return ret;
  19. }

为了验证跑出来的数据对不对,再写一个暴力求[0,n]的程序,这查错的办法。

  1. LL check(int n,int k)
  2. {
  3. LL ret=;
  4. int t;
  5. for(int i=;i<=n;i++){
  6. t=i;
  7. while(t){
  8. ret+=t%k;
  9. t/=k;
  10. }
  11. }
  12. return ret;
  13. }

完整程序:

  1. #include <stdio.h>
  2. typedef long long LL;
  3.  
  4. LL sum1(int pre,int n,int k)
  5. {
  6. LL ret=;
  7. LL pw=;
  8. for(int i=;i<n;i++) pw*=k;
  9. ret=pre*pw+pw*n*(k-)/;
  10. return ret;
  11. }
  12.  
  13. LL check(int n,int k)
  14. {
  15. LL ret=;
  16. int t;
  17. for(int i=;i<=n;i++){
  18. t=i;
  19. while(t){
  20. ret+=t%k;
  21. t/=k;
  22. }
  23. }
  24. return ret;
  25. }
  26.  
  27. LL sum2(int pre,LL n,int k)
  28. {
  29. if(n<k){
  30. LL ret=;
  31. for(int i=;i<=n;i++) ret+=pre+i;
  32. return ret;
  33. }
  34. LL tn=n,pw=,ret=;
  35. int mi=;
  36. while(tn>=k){
  37. pw*=k;
  38. mi++;
  39. tn/=k;
  40. }
  41. for(int i=;i<tn;i++)
  42. ret+=sum1(pre+i,mi,k);
  43. ret+=sum2(pre+tn,n-tn*pw,k);
  44. return ret;
  45. }
  46.  
  47. int main()
  48. {
  49. LL n;
  50. int k;
  51. while(~scanf("%I64d %d",&n,&k)){
  52. printf("%I64d\n",sum2(,n,k));
  53. printf("%I64d\n",check(n,k));
  54. }
  55. return ;
  56. }

数位DP 求K进制下0~N的每个数每位上出现的数的总和的更多相关文章

  1. n!在k进制下的后缀0

    问n! 转化成k进制后的位数和尾数的0的个数.[UVA 10061 How many zeros and how many digits?] Given a decimal integer numbe ...

  2. 求x!在k进制下后缀零的个数(洛谷月赛T1)

    求x!在k进制下后缀和的个数 20分:     求十进制下的x!后缀和的个数 40分: 高精求阶乘,直接模拟过程 (我不管反正我不打,本蒟蒻最讨厌高精了) 60分     利用一个定理(网上有求x!在 ...

  3. bzoj 3000 Big Number 估算n!在k进制下的位数 斯特林公式

    题目大意 求n!在k进制下的位数 2≤N≤2^31, 2≤K≤200 分析 作为数学没学好的傻嗨,我们先回顾一下log函数 \(\log_a(b)=\frac 1 {log_b(a)}\) \(\lo ...

  4. 51 Nod 1116 K进制下的大数

    1116 K进制下的大数  基准时间限制:1 秒 空间限制:131072 KB 分值: 20 难度:3级算法题  收藏  关注 有一个字符串S,记录了一个大数,但不知这个大数是多少进制的,只知道这个数 ...

  5. PAT 1015 Reversible Primes[求d进制下的逆][简单]

    1015 Reversible Primes (20)(20 分)提问 A reversible prime in any number system is a prime whose "r ...

  6. 陕西师范大学第七届程序设计竞赛网络同步赛 F WWX的礼物【数学/k进制下x^n的位数/log】

    链接:https://www.nowcoder.com/acm/contest/121/F来源:牛客网 题目描述 WWX的女朋友送给了他一个礼物,可是礼物却被一把K进制密码锁锁住了.在礼物盒上还有一张 ...

  7. [51nod1116]K进制下的大数

    解题关键:$A\% (k - 1) = (A[0] + A[1]*k + A[2]*{k^2} + ...A[n]*{k^n})\% (k - 1) = (A[0] + A[1] + ...A[n]) ...

  8. 51nod 1116 K进制下的大数

    你万万想不到,Long Long 就能存下的数据 #include <iostream> #include <cstdio> #include <cstdlib> ...

  9. A - K进制下的大数

    https://vjudge.net/contest/218366#problem/A 中间溢出,注意求模. #include<iostream> #include<cstdio&g ...

随机推荐

  1. 前端自动化工具 -- fis 使用简介

    https://github.com/fex-team/fis FIS入门: http://fis.baidu.com/docs/beginning/getting-started.html FIS ...

  2. Stereo Matching 立体匹配学习资料

    Middlebury Stereo Evaluation Camera Calibration and 3D Reconstruction OpenCV学习笔记(18)双目测距与三维重建的OpenCV ...

  3. 数据库存储ATM机,开户、查询等信息

    package com.bank.unionpay; //银行卡的接口 public interface I_yinhangka { //抽象方法 //public abstract默认修饰抽象的 p ...

  4. JAVA集合迭代遍历和特性介绍

    数组.集合:都是一种容器,用一个对象管理多个对象:数组不能自动增长:只能存放同类型的元素 集合能自动扩容:部分集合允许存放不同类型的元素: 1.List: 有顺序的,允许存放重复的元素: 遍历:for ...

  5. A quick tour of JSON libraries in Scala

    A quick tour of JSON libraries in Scala Update (18.11.2015): added spray-json-shapeless libraryUpdat ...

  6. thinkphp 的create()非法数据解决办法

    是因为create()方法默认是使用post传值的,把from表单的传值方法改成post就行了,默认是get.

  7. wget 下载整个网站,或者特定目录

    需要下载某个目录下面的所有文件.命令如下 wget -c -r -np -k -L -p www.xxx.org/pub/path/ 在下载时.有用到外部域名的图片或连接.如果需要同时下载就要用-H参 ...

  8. sql*loader的直接加载方式和传统加载方式的性能差异

    1.确认数据库版本 2.数据准备 3.创建导入表及控制文件 4.直接加载方式演示 查看具体的日志: 5.传统加载方式演示 查看日志文件: 6.结论及两种方式的差异 经过比对direct比convent ...

  9. 通过Queue的构造函数的可选参数maxsize来设定队列长度

    创建一个"队列"对象 import Queuemyqueue = Queue.Queue(maxsize = 10) Queue.Queue类即是一个队列的同步实现.队列长度可为无 ...

  10. 外部引用JavaScript文件乱码问题

    使用js外部文件输出中文乱码解决: 将js文件编码改为和页面的编码相同.