题目链接:https://www.luogu.com.cn/problem/P1980

术语

以下的英文术语均可以翻译为数字。

  • digit: 一个数字字符,十进制就是 0-9 之间的一个字符;
  • numeral: 用来表示数字的符号化表示,如 “Three”、“3”、“III”;
  • number: 一种抽象的数字概念。

为了区分,下文中均使用英文术语。

注意:关于 numeral 和 number 区别见: 如何在英文写作中正确的表达数字?

十进制数表示法

十进制数(decimal numeral)由一串有限长度的 digit 序列组成,

\[a_ma_{m-1}\ldots a_0
\]

其中,\(a_i \in \{0,1,2,3,4,5,6,7,8,9\}\)。

numeral \(a_ma_{m-1}\ldots a_0\) 表示了 number \(a_m10^m+a_{m-1}10^{m-1}+\cdots+a_{0}10^0\)。

注意:关于十进制数的详细介绍见 Wikipedia Decimal

前置 \(0\)

为简化问题,我们假设我们的 numeral 可以出现前置 \(0\),即,从最左侧开始,可以出现多个连续的 \(0\),如 \(03\),\(003\)。通过前置 \(0\), 我们可以使用三个 digit 来包含一位数、二位数、三位数的 number

三个 digit

为简化问题,我们目前只考虑三位数之内的 number,且忽略 \(n\)(目前可视作 \(n = 999\),用来包含所有的一位数、二位数、三位数)。

出现次数是?

根据题目,我们需要统计出 digit \(x\) 在所有 numeraldigit 中出现的次数。这里我们列出部分的 numeral

\[\begin{array}
{c|c|c}
a_2&a_1&a_0\\
\hline
0&0&0\\
0&0&1\\
0&0&2\\
0&0&3\\
\vdots&\vdots&\vdots\\
9&9&6\\
9&9&7\\
9&9&8\\
9&9&9\\
\end{array}
\]

令 \(C_i\) 等于第 \(i\) 位上 \(x\) 出现的总次数,那么\(x\) 在所有 numeraldigit 中出现的次数为 \(S=\sum_{i=0}^2C_i\)。

\(C_i\) 等于?

我们通过 \(x = 1\) 来进行说明。首先我们令 \(a_2 = 1\),此时百位上的数字已经固定,\(a_1\) 和 \(a_0\) 分别有 \(0\) 到 \(9\) 十种可能,且彼此互不影响,所以 \(C_2 = 10 \times 10 = 100\),同理我们得到 \(C_0=C_1=100\)。从而,我们最终得到 \(S=100+100+100=300\)。

通过上面的方式,我们可以得到任意 \(x\) 对应的 \(S\)。

注意:digit \(0\) 比较特殊,我们这里暂时未考虑。

考虑 \(n\) 呢?

上面的问题只看 digit 的个数,但忽略了 \(n\)。如果我们也要将 \(n\) 考虑在内呢?

\(n=123\), \(x=3\)

这里以 \(n=123\),\(x=3\) 进行举例。首先我们可以知道 \(a_2 = 3\) 是不可能的,所以此时 \(C_2 = 0\)。下面我们考虑 \(a_1 = 3\),此时 \(a_2\) 必须小于 \(1\), 所以只有 \(a_2 = 0\) 一种可能,由于向前面借了一位, \(a_0\) 可以包含 \(0\) 到 \(9\) 十种可能,从而我们得到 \(C_1 = 10\)。然后我们考虑 \(a_0 = 3\),当 \(a_2 = 1\) 时,需满足 \(a_1 \leqslant 2\);当 \(a_2 = 0\) 时,\(a_1\) 可以包含 \(0\) 到 \(9\) 十种可能,加起来我们有 \(C_0 = 3 + 10 = 13\) 种可能。最终,我们得到 \(S=0+10+13=23\)。

\(n=123\), \(x=2\)

这里以 \(n=123\),\(x=2\) 进行举例。首先我们可以知道 \(a_2 = 2\) 是不可能的,所以此时 \(C_2 = 0\)。下面我们考虑 \(a_1 = 2\),此时 \(a_2\) 有 \(0\) 和 \(1\) 两种可能,当 \(a_2 = 1\), 需满足 \(a_1 \leqslant 3\);当 \(a_2 = 0\), \(a_1\) 可以包含 \(0\) 到 \(9\) 十种可能,从而我们得到 \(C_1 = 4 + 10 = 14\)。然后我们考虑 \(a_0 = 2\),当 \(a_2 = 1\) 时,需满足 \(a_1 \leqslant 2\);当 \(a_2 = 0\) 时,\(a_1\) 可以包含 \(0\) 到 \(9\) 十种可能,加起来我们有 \(C_0 = 3 + 10 = 13\) 种可能。最终,我们得到 \(S=0+14+13=27\)。

\(n=123\), \(x=1\)

这里以 \(n=123\),\(x=1\) 进行举例。首先我们令 \(a_2=1\),当 \(a_1=2\) 时,需满足 \(a_0 \leqslant 3\);当 \(a_1 \leqslant 1\) 时,\(a_0\) 可以包含 \(0\) 到 \(9\) 十种可能,故 \(C_2 = 4 + 10 + 10 = 24\)。下面我们考虑 \(a_1 = 1\),此时 \(a_2\) 有 \(0\) 和 \(1\) 两种可能,当 \(a_2 \leqslant 1\), \(a_0\) 可以包含 \(0\) 到 \(9\) 十种可能,从而我们得到 \(C_1 = 10 + 10 = 20\)。然后我们考虑 \(a_0 = 1\),当 \(a_2 = 1\) 时,需满足 \(a_1 \leqslant 2\);当 \(a_2 = 0\) 时,\(a_1\) 可以包含 \(0\) 到 \(9\) 十种可能,加起来我们有 \(C_0 = 3 + 10 = 13\) 种可能。最终,我们得到 \(S=24+20+13=57\)。

我们发现上面的统计方式,通过程序来处理的话比较麻烦,最好一种统一的方式来处理各种情况,

移除 digit \(x\),再看看!

  • \(x > a_i\),

    当 \(x > a_i\),我们令 \(a_{i-1} \gets a_{i-1} - 1\),\(a_k \gets 9 \text{ for } k > i\)。比如对于 \(n = 123\), \(x=3\),\(i = 2\),我们得到 \(039\),我们移除 \(a_2\) 得到 \(09\),在对 \(09 + 1\) 得到 \(10\),我们发现这正好等于 \(C_2\);

  • \(x = a_i\),

    当 \(x = a_i\),不进行额外操作。比如对于 \(n = 123\), \(x=2\),\(i = 2\),我们得到 \(123\),我们移除 \(a_2\) 得到 \(13\),在对 \(13 + 1\) 得到 \(14\),我们发现这也正好等于 \(C_2\);

  • \(x < a_i\),

    当 \(x = a_i\),我们令 \(a_k \gets 9 \text{ for } k > i\)。比如对于 \(n = 123\), \(x=1\),\(i = 2\),我们得到 \(119\),我们移除 \(a_2\) 得到 \(19\),在对 \(19 + 1\) 得到 \(20\),我们发现这又正好等于 \(C_2\)。

上面的方法可以总结为,

  1. 首先判断 \(x\) 和 \(a_i\) 的大小关系;
  2. 根据判断结果来对其他的 digit 进行修改;
  3. 移除 \(x\) 对应的 \(a_i\),得到一个新的 numeral
  4. 计算 numeral 对应的 number
  5. 最后在对得到的结果 \(+1\) 即可。

注意:如果 \(a_i\) 最高位,且 \(x > a_i\),那么此时没有可以借位的 digit,所以 \(C_i = 0\)。

注意:digit \(0\) 比较特殊,我们这里暂时未考虑。

多出来 \(0\)?

如果我们按照前面的方法对 \(x = 0\) 进行统计,我们会发现 \(0\) 的次数被多统计了一些。

为了解释这个问题,这里我们再次以三个 digitnumeral 进行说明。

对于任意一个三位数的 \(n\),我们之前的方法会把所有有一个前置 \(0\) 和有两个前置 \(0\) 的 numeral 包含进来,比如 \(010\),\(001\)。而这些前置 \(0\) 实际上不应该被统计进来,为了得到正确的答案,我们可以在最终结果的地方减去多统计的前置 \(0\) 。

前置 \(0\) 的个数

前置 \(0\) 的个数为 \(a_2 = 0\) 的 numeral 个数,以及 \(a_2=0\) 同时 \(a_1 = 0\) 的numeral 个数,因为题目中 number 不包含 \(0\),所以我们最终还要加上 \(a_2=0\) 同时 \(a_1 = 0\) 同时 \(a_0 = 0\) 的情况。最终我们可以得到多统计的前置 \(0\) 个数为 \(100 + 10 + 1 = 10^2 + 10^1 + 10^0 = 111\)。

由此我们可以推广 \(m\) 个 digitnumeral 多统计的前置 \(0\) 为 \(\sum_{i=0}^{m-1} 10^i\)。

结论

  • \(x \ne 0\) 时,采用移除 digit \(x\) 的方法即可。
  • \(x = 0\) 时,采用移除 digit \(x\) 方法的同时还需要减去多余的前置 \(0\)。

代码如下:

// https://www.luogu.com.cn/problem/P1980
// https://www.cnblogs.com/revc/p/17087297.html #include <iostream>
#include <cmath> int main()
{
int n, x; std::cin >> n >> x; int digits[7]; int m = 0; // the number of digits
while(n > 0) // NOTE: store digits in reverse order
{
digits[m++] = n % 10;
n /= 10;
} int S = 0;
for (int i = 0; i < m; i++)
{
int C = 0;
int borrow = 0;
if (x > digits[i])
{
if (i+1 == m) continue; /* fail to borrow */
borrow = 1;
digits[i+1]--;
} for (int j = m - 1; j > i; j--)
{
C = C * 10 + digits[j];
} for (int j = i - 1; j >= 0; j--)
{
C = C * 10 + (digits[i]==x ? digits[j] : 9);
} digits[i+1] += borrow;
S += C + 1;
} if (x == 0)
{
int subtrahend = 1;
while(m--)
{
S -= subtrahend;
subtrahend *= 10;
}
} std::cout << S << std::endl; return 0;
}

P1980 [NOIP2013 普及组] 计数问题的更多相关文章

  1. 洛谷——P1980 [NOIP2013 普及组] 计数问题

    题目描述 试计算在区间 11 到 nn的所有整数中,数字x(0 ≤ x ≤ 9)x(0≤x≤9)共出现了多少次?例如,在 11到1111中,即在 1,2,3,4,5,6,7,8,9,10,111,2, ...

  2. [NOIP2013] 普及组

    计数问题 纯模拟 #include<cstdio> #include<iostream> using namespace std; int main(){ int n,x; c ...

  3. NOIP2013普及组 -SilverN

    T1  计数问题 题目描述 试计算在区间 1 到 n 的所有整数中,数字 x(0 ≤ x ≤ 9)共出现了多少次?例如,在 1 到 11 中,即在 1.2.3.4.5.6.7.8.9.10.11 中, ...

  4. NOIP2013普及组 T2 表达式求值

    OJ地址:洛谷P1981 CODEVS 3292 正常写法是用栈 #include<iostream> #include<algorithm> #include<cmat ...

  5. 【NOIP2013 普及组】车站分级

    [NOIP2013 普及组]车站分级 一.题目 [NOIP2013 普及组]车站分级 时间限制: 1 Sec  内存限制: 128 MB 提交: 3  解决: 0 [提交][状态][讨论版] 题目描述 ...

  6. [NOIP2013 普及组] 表达式求值

    [NOIP2013 普及组] 表达式求值 给定一个只包含加法和乘法的算术表达式,请你编程计算表达式的值. Input 一行,为需要你计算的表达式,表达式中只包含数字.加法运算符"+" ...

  7. [NOIp2013普及组]车站分级

    思路: 对于每一趟车,将区间内所有经停的站和所有未经停的站连一条边,表示前者优先级一定高于后者,然后用Kahn跑一遍拓扑排序即可.然而这样会创造大量多余的边,会TLE1个点.考虑一种优化:因为每趟车本 ...

  8. Noip2013(普及组) 车站分级

    题目描述 一条单向的铁路线上,依次有编号为 , , …, n 的 n 个火车站.每个火车站都有一个级别,最低为 级.现有若干趟车次在这条线路上行驶,每一趟都满足如下要求:如果这趟车次停靠了火车站 x, ...

  9. NOIP2002-2017普及组题解

    虽然普及组一般都是暴力省一,但是有一些题目还是挺难的qwq个人觉得能进TG的题目会在前面打上'*' NOIP2002(clear) #include<bits/stdc++.h> usin ...

  10. NOIP2012 普及组 T3 摆花——S.B.S.

    题目描述 小明的花店新开张,为了吸引顾客,他想在花店的门口摆上一排花,共m盆.通过调查顾客的喜好,小明列出了顾客最喜欢的n种花,从1到n标号.为了在门口展出更多种花,规定第i种花不能超过ai盆,摆花时 ...

随机推荐

  1. 关于Unity 图片队列存储以及出列导致内存溢出的解决方案

    图片虽然出列但是并没有销毁,所以..destroy !  

  2. MQ异常断开

    ActiveMQ:No operations allowed after statement closed问题及解决办法   ActiveMQ版本:5.5.1 现象: 系统现象:部分消息发送失败,失败 ...

  3. Adams:导出动画

    1 首先模型在adams里能正常运动,点击start simulation仿真一遍. 2 然后在界面上按F8进入Plotting界面. 3 在左上角把Plotting换成Animation. 4 然后 ...

  4. finereport连接sql server

    1.添加jdbc 2.选择sql server 3.jdbc:sqlserver://localhost:1433(localhost=其他IP地址)

  5. Jsp 总结

    JSP中的include的两种用法 1.两种用法 <@inlcude file ="header.jsp"/> 此时引入的是静态的jsp文件,它将引入的jsp中的源代码 ...

  6. axios utils axios.postForm

    npm install axios -S 目录 ajax.js   请求工具 /* ajax请求函数模块 返回值: promise对象(异步返回的数据是: response.data) */ impo ...

  7. 快速掌握Linux三剑客命令使用

    前言 Linux三剑客指的是grep.sed以及awk命令的使用,这三个命令功能异常强大,大到没朋友.grep命令主打"查找",sed命令主打"编辑",awk命 ...

  8. Spring Cloud Alibaba实现服务的无损下线功能

    目录 1.背景 2.解决方案 2.1 找到通过负载均衡组件获取可用服务信息的地方 2.2 解决思路 3.部分实现代码 3.1 引入jar 3.2 编写服务下线方法 3.3 监听配置变更,清除服务缓存 ...

  9. Java项目是不是分布式,真有那么重要吗?

    大家好,我是3y啊. 大概不知道从什么时候,「微服务」「分布式」这两个词又再次频繁出现在我的视线里. 「微服务」「分布式」在我刚毕业的时候还是比较关注的,那时候还入门了一把SpringCloud,写了 ...

  10. 自己动手从零写桌面操作系统GrapeOS系列教程——19.硬盘读写理论知识

    学习操作系统原理最好的方法是自己写一个简单的操作系统. 一.硬盘控制器 我们前面已经讲过硬盘控制器是一种I/O接口,CPU通过它就能间接的读写硬盘.硬盘控制器主要有IDE和SATA两种,我们这里只考虑 ...