题面:

你有一组非零数字(不一定唯一),你可以在其中插入任意个0,这样就可以产生无限个数。比如说给定{1,2},那么可以生成数字12,21,102,120,201,210,1002,1020,等等。

现在给定一个数,问在这个数之前有多少个数。(注意这个数不会有前导0).

样例输入:1020     样例输出:7

题解:

刚看到这道题的时候有点懵,,,,,

其实仔细观察一下发现这题可以用组合做。

注意到0的个数是不限的,

而且如果位数小于给定n的话,肯定可以随便搭配,

所以直接加上给定数的有效位+补全的0的全排列就好了,(全排列注意不能重复)

有效位指的是不为0的位,

这样虽然有前导0,但是可以看做是位数少的数,比如

0012,0120分别可以看做12和120,

这样不仅不用去掉前导0,还可以只用加一次,非常方便

因此我们要求就只剩下位数相同的方案了,

这就要用到数位DP了

我们从前往后枚举,因为如果确定的当前位小于给定n的当前位的话,

由于是从前往后枚举,只要当前位小了,后面的位不管怎么排列都是小于n的,

因此我们加上去掉已经被确定的数后的全排列(全排列不能重复)就可以了,

如果当前位相同,那方案数就要放在后面求(注意每确定一位都要在有的数集合中减去它)

注意在第一位的时候不要枚举0就可以了

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define R register int
  4. #define AC 60
  5.  
  6. #define LL long long
  7. LL len,ans,tot;
  8. int num[AC],have[AC],use[AC];
  9. /*第i位是什么,在哪里?
  10. 设给定n的位数为len,那么位数小于len的显然可以随意组合,
  11. 于是利用有效位组合计算。
  12. 位数大于len的显然不合法
  13. 所以只要计算位数等于len的即可,
  14. 假设n的第i位不为0,那么如果用比第i位小的数摆在第i位上,
  15. 这个数就肯定比n小了,所以后面就可以随意搭配了
  16. 所以利用组合来算?
  17. 之前只想到位数小于len的可以组合算,
  18. 原来确定了前面的后面也可以组合算啊(废话),
  19. 如果g[i]表示i位数,自由组合的合法方案,i>=有效位
  20. 则g[i] = i! - (i-1)!
  21. 看上去怎么这么玄啊。。。。
  22. 果然是错的,,,因为如果有效位中有重复的话,这一部分就会被重复计算,
  23. 比如26637,那么对于每一种合法排列,都会计算2次,因为6可以交换
  24. 所以要去重,如果数i的出现次数为have[i],那么它会使得整个数列被多计算have[i]!次,
  25. 同理,拓展到所有数,整个数列会被多计算have[i]! * have[i+1]! + .......
  26. 因此设f[i]表示长度为i时的排列(无重复,有前导零)
  27. g[i]表示长度为i时的排列(无重复,无前导零)
  28. 那么g[i] = f[i] - f[i-1];//相当于确定了第1位是0时,就必然有前导零了.
  29. 设比当前位i大的数有k位,
  30. 那么ans += k * newf[n-i];
  31. 因为这时前几位已经确定,所以have和tot都变了,因此要用newf,而不是f
  32. 因此可以发现,前面求的g和f到后面根本就没有用,所以还是不要求了,,,,,
  33. 直接算就好了
  34. ans += tot!/have[1]! * have[2]!.....
  35. */
  36. void pre()
  37. {
  38. char c=getchar();
  39. while(c > '' || c < '') c=getchar();
  40. while(c >= '' && c <= '')
  41. {
  42. num[++len]=c - '',c=getchar();
  43. if(num[len] != ) ++have[num[len]],++tot;//统计有效位
  44. }//读入
  45. have[]=len - tot - ;//获取0的个数
  46. if(!tot)
  47. {
  48. printf("0\n");
  49. exit();
  50. }
  51. }
  52.  
  53. LL cal(int cnt)
  54. {
  55. LL tmp=;
  56. /* for(R i=1;i<=9;i++) tmp+=have[i],use[i]=have[i];
  57. use[0]=cnt - tmp;//获取0的个数,因为work中获取了,所以不用再次获取*/
  58. for(R i=;i<=;i++) use[i] = have[i];
  59. tmp=;
  60. for(R i=;i<=cnt;i++)
  61. {
  62. tmp*=i;
  63. for(R j=;j<=;j++)
  64. {
  65. if(use[j] <= ) continue;
  66. while(!(tmp % use[j]))
  67. {
  68. tmp /= use[j];
  69. --use[j];
  70. if(use[j] == ) break;
  71. }
  72. }
  73. }
  74. return tmp;
  75. }
  76.  
  77. void work()
  78. {
  79. LL tmp;
  80. have[]++;//把之前为了计算小于len位的答案而减掉的0加回来
  81. for(R i=;i<=len;i++)
  82. {
  83. tmp=;
  84. for(R j=;j<=;j++) tmp+=have[j];
  85. have[]=(len - i + ) - tmp;//获取0的个数
  86. int b=(i == );//因为第一位不能用0代替
  87. for(R j=b;j<num[i];j++)//枚举放哪个,但只能尝试比当前位小的,且这样还可以保证不重复
  88. {
  89. if(!have[j]) continue;//没有就不能放
  90. --have[j];//先减掉
  91. /*if(i == 1)//error!!!但是只要第一个不是0的话,,,,后面的就不是前导零啊,,,有什么关系。。。。
  92. {//求g值,如果第一个是0的话,其他的have不会变,have[0]又是临时求的,所以没关系
  93. ans += cal(len - i);
  94. --have[0];
  95. ans -= cal(len - i -1);
  96. ++have[0];
  97. }*/
  98. ans+=cal(len - i);//只有第一位才不能有前导0,,,,
  99. ++have[j];
  100. }
  101. if(num[i]) have[num[i]]--;//假设这一位也相同,那么就要减掉,0的时候不用减,因为0是临时算的(不过应该也可以不临时算?)
  102. }
  103. printf("%lld\n",ans);
  104. }
  105.  
  106. int main()
  107. {
  108. // freopen("in.in","r",stdin);
  109. pre();
  110. if(len - >= tot) ans+=cal(len-);//这样会有前导零,但可以看做是位数不同
  111. work();
  112. // fclose(stdin);
  113. return ;
  114. }

[HAOI2010]计数 数位DP+组合数的更多相关文章

  1. BZOJ2425:[HAOI2010]计数(数位DP)

    Description 你有一组非零数字(不一定唯一),你可以在其中插入任意个0,这样就可以产生无限个数.比如说给定{1,2},那么可以生成数字12,21,102,120,201,210,1002,1 ...

  2. BZOJ 2425 [HAOI2010]计数:数位dp + 组合数

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2425 题意: 给你一个数字n,长度不超过50. 你可以将这个数字: (1)去掉若干个0 ( ...

  3. 【BZOJ-1833】count数字计数 数位DP

    1833: [ZJOI2010]count 数字计数 Time Limit: 3 Sec  Memory Limit: 64 MBSubmit: 2494  Solved: 1101[Submit][ ...

  4. bzoj1833: [ZJOI2010]count 数字计数(数位DP+记忆化搜索)

    1833: [ZJOI2010]count 数字计数 题目:传送门 题解: 今天是躲不开各种恶心DP了??? %爆靖大佬啊!!! 据说是数位DP裸题...emmm学吧学吧 感觉记忆化搜索特别强: 定义 ...

  5. 【题解】P2602 数字计数 - 数位dp

    P2602 [ZJOI2010]数字计数 题目描述 给定两个正整数 \(a\) 和 \(b\) ,求在 \([a,b]\) 中的所有整数中,每个数码(digit)各出现了多少次. 输入格式 输入文件中 ...

  6. BZOJ 1833 数字计数 数位DP

    题目链接 做的第一道数位DP题,听说是最基础的模板题,但还是花了好长时间才写出来..... 想深入了解下数位DP的请点这里 先设dp数组dp[i][j][k]表示数位是i,以j开头的数k出现的次数 有 ...

  7. bzoj1833: [ZJOI2010]count 数字计数 数位dp

    bzoj1833 Description 给定两个正整数a和b,求在[a,b]中的所有整数中,每个数码(digit)各出现了多少次. Input 输入文件中仅包含一行两个整数a.b,含义如上所述. O ...

  8. NEUQ OJ 2004:追梦之人 (计数数位dp)

    2004: 追梦之人 描述 题目描述: 为了纪念追梦人,粉丝们创造了一种新的数——“追梦数”.追梦数要满足以下两个条件:1.数字中不能出现“7”2.不能被7整除.比如:777和4396就不是追梦数,而 ...

  9. LightOJ 1140 计数/数位DP 入门

    题意: 给出a,b求区间a,b内写下过多少个零 题解:计数问题一般都会牵扯到数位DP,DP我写的少,这道当作入门了,DFS写法有固定的模板可套用 dp[p][count] 代表在p位 且前面出现过co ...

随机推荐

  1. DXF结构查看小工具,DXF表格导出工具,CAD文档查看

    用C#写了个查看DXF结构的工具,另做了个DXF表格(普通直线画的)导出为CSV表格工具发出来方便各位机械工程师,上几个图: 程序下载: 程序,需要.NET 4.0执行环境 https://pan.b ...

  2. unity3d 计时功能舒爽解决方案

    上次也写了一篇计时功能的博客 今天这篇文章和上次的文章实现思路不一样,结果一样 上篇文章地址:http://www.cnblogs.com/shenggege/p/4251123.html 思路决定一 ...

  3. 阿里云ECS下Ubuntu 16.04系统安装python3.6.5 环境并设置为默认

    一.添加python3.6安装包并安装: 二.修改系统默认python版本为3.6: 三.安装并升级pip版本: 一.添加python3.6安装包并安装: sudo apt-get install s ...

  4. HPUX 11.31 MC/SG恢复丢失的锁盘

    有时候由于一些特殊的原因,用户的cluster中的锁盘信息丢失,或者需要更换锁盘,只要执行一个命令就可以了. #cmdisklock reset /dev/vglock:/dev/disk/diskX ...

  5. python常用命令—查看模块所在位置

    环境:ipython3 交互式解释器 语法: import 模块名 模块名.__file__ 功能: 查看模块的所在位置 例:

  6. [leetcode-744-Find Smallest Letter Greater Than Target]

    Given a list of sorted characters letters containing only lowercase letters, and given a target lett ...

  7. SGU 326 Perspective(最大流)

    Description Breaking news! A Russian billionaire has bought a yet undisclosed NBA team. He's plannin ...

  8. CP文件覆盖问题

    # \cp -r -a aaa/* /bbb[这次是完美的,没有提示按Y.传递了目录属性.没有略过目录]

  9. fragment的介绍与使用

    稍稍摘录一段Fragment.java中的说明文档. /** * A Fragment is a piece of an application's user interface or behavio ...

  10. 计算器软件实现系列(七)WPF+SQL+策略模式

    一  整体概述 本次设计主要是在WPF的页面中实现的,属于表现层的更换,数据库部分用的还是数据库的封装,其中引用了策略模式 二  设计思路 1 在出题页面,进行试题的编辑,在编辑后会自动保存到数据库中 ...