计数问题
2022/2/7 6:12:30
本文主要是介绍计数问题,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
计数问题
P1179 [NOIP2010 普及组] 数字统计
题目描述
请统计某个给定范围[L,R]的所有整数中,数字2出现的次数。比如给定范围[2,22],数字2在数2中出现了1次,在数12中出现1次,在数20中出现1次,在数21中出现>1次,在数22中出现2次,所以数字2在该范围内一共出现了6次。
输入格式
2个正整数L和R,之间用一个空格隔开。输出格式
数字2出现的次数。
说明/提示
1≤L≤R≤100000
1. 枚举法
只要遍历\([L,R]\)的每个数的每位数字进行统计就可得到答案,复杂度是\(O(nlogn)\)。
完整代码
#include <iostream> using namespace std; int main(){ int l,r; cin>>l>>r; int cnt = 0; for(int i = l;i<=r;i++){ int tmp = i; while(tmp){ if(tmp%10 == 2) cnt++; tmp/=10; } } cout<<cnt<<endl; return 0; }
这个复杂度对于这道题的数据量而言已经够了,但是数据量超过 \(10^8\) 量级后就不可接受了,需要用到公式法。
2.公式法
假设 \(f(n,num)\) 是从 \([1,n]\) 所有整数出现数字 \(num\) 的次数和,那么想要得到 \([L,R]\) 内所有整数出现数字 \(num\) 的次数和可以通过 \(f(r,num)-f(l-1,num)\) 得到,其中 \(0\leq num\leq 1\) 。
我们可以通过考虑每位数字得到 \(f(n,num)\) :
\(1\leq num\leq 9\) 的情况
假设一个 \(p\) 位正整数 \(n\) 的数位构成情况为 \(\overline{n_{p-1}n_{p-2}\cdots n_1n_0}\) ,其中 \(n_i\) 是第 \(i\) 位数的数字 \((0\leq i\leq p-1)\) 。
考虑 \([1,n]\) 中某个数 \(m\) 其 \(p\) 位构成情况为 \(\overline{m_{p-1}m_{p-2}\cdots m_1m_0}\) (不够p位数可以补前导0)。
- \(n_i<num\)
- 若 \(m\) 的 \(i+1\) 到 \(p-1\) 位的数字构成的新数小于 \(n\) 的 \(i+1\) 到 \(p-1\) 位的数字构成的新数,即 $$\overline{m_{p-1}\cdots m_{i+1}}<\overline{n_{p-1}\cdots n_{i+1}}$$
那么 \(m\) 可以满足 \(m_i=num\) 且无论第 \(i-1\) 位及以后的数字如何也一定在 \([1,n]\) 中。 - 当 $$\overline{m_{p-1}\cdots m_{i+1}}\geq \overline{n_{p-1}\cdots n_{i+1}}$$ 因为 \(ni<num\) ,如果此时使 \(m_i=num\) ,那么 $$\overline{m_{p-1}\cdots m_{i+1}m_i}>\overline{n_{p-1}\cdots n_{i+1}n_i}$$ 则无论 \(m\) 第 \(i-1\) 位及以后的数字如何都会 \(m>n\) 从而不在 \([1,n]\) 的范围中。
- 综上,当 \(n_i<num\) 只有 $$\overline{m_{p-1}\cdots m_{i+1}}<\overline{n_{p-1}\cdots n_{i+1}}$$ 时成立,且此时 \([1,n]\) 第 \(i\) 位出现 \(num\) 的次数为 $$\overline{n_{p-1}\cdots n_{i+1}}\times10^i$$
- \(n_i=num\)
- 当 $$\overline{m_{p-1}\cdots m_{i+1}}<\overline{n_{p-1}\cdots n_{i+1}}$$ 时,次数 $$\overline{n_{p-1}\cdots n_{i+1}}\times 10^i$$ 显然成立。
- 当 $$\overline{m_{p-1}\cdots m_{i+1}}=\overline{n_{p-1}\cdots n_{i+1}}$$时,若 \(m\) 满足 \(m_i=num\) ,那么只要 $$\overline{m_{i-1}\cdots m_0}\leq \overline{n_{i-1}\cdots n_0}$$ 即可,此时次数为 $$\overline{n_{i-1}\cdots n_0}+1$$ 我们约定 \(i=0\) 时,$$\overline{n_{i-1}\cdots n_0}=0$$
- 当 $$\overline{m_{p-1}\cdots m_{i+1}}> \overline{n_{p-1}\cdots n_{i+1}}$$ 那么无论 \(m\) 第 \(i-1\) 位及以后的数字如何都会 \(m>n\) ,从而不在 \([1,n]\) 的范围中。
- 综上,当 \(n_i\geq num\) 只有 $$\overline{m_{p-1}\cdots m_{i+1}}\leq \overline{n_{p-1}\cdots n_{i+1}}$$ 时成立,且此时 \([1,n]\) 第 \(i\) 位出现 \(num\) 的次数为 $$\overline{n_{p-1}\cdots n_{i+1}}\times 10^i+\overline{n_{i-1}\cdots n_0}+1$$
- \(n_i>num\)
- 当 $$\overline{m_{p-1}\cdots m_{i+1}}<\overline{n_{p-1}\cdots n_{i+1}}$$ 时,次数 $$\overline{n_{p-1}\cdots n_{i+1}}\times 10^i$$ 显然成立;
- 当 $$\overline{m_{p-1}\cdots m_{i+1}}=\overline{n_{p-1}\cdots n_{i+1}}$$时,因为 \(n_i>num\) ,若使 \(m_i=num\),则 $$\overline{m_{p-1}\cdots m_{i+1}m_i}<\overline{n_{p-1}\cdots n_{i+1}n_i}$$ 从而无论第 \(i-1\) 位及以后的数字如何也一定在 \([1,n]\) 中,次数为 \(10^i\) 。
- 当 $$\overline{m_{p-1}\cdots m_{i+1}}> \overline{n_{p-1}\cdots n_{i+1}}$$ 那么无论 \(m\) 第 \(i\) 位及以后的数字如何都会 \(m>n\) ,从而不在 \([1,n]\) 的范围中。
- 综上,当 \(n_i\geq num\) 只有 $$\overline{m_{p-1}\cdots m_{i+1}}\leq \overline{n_{p-1}\cdots n_{i+1}}$$ 时成立,且此时 \([1,n]\) 第 \(i\) 位出现 \(num\) 的次数为 $$(\overline{n_{p-1}\cdots n_{i+1}}+1)\times 10^i$$
\(num=0\)的情况
这种情况下,我们知道如果某一位是 \(0\) ,那么这个整数一定有不为 \(0\) 的更高位,就需要排除在上一种情况下 \(\overline{m_{p-1}\cdots m_{i+1}}=0\) 的一种可能了。显然在三个可能的 \(num\) 和 \(n_i\)关系下,都可能出现这种情况,所以在每次计算完之后需要减去一个关于 \(\overline{n_{p-1}\cdots n_{i+1}}\) 的所有情况,即减去 \(10^i\) 。
运用以上的结论,每次只需要遍历一次两个端点的位数,是 \(O(logn)\)的算法。
完整代码
#include <iostream> #define ll long long using namespace std; ll f(ll n,ll num){ ll base = 1,cnt = 0; while(base<=n){ ll pre = n/(base*10); ll suf = n%base; ll now = n/base%10; if(now<num) cnt+=pre*base; else if(now == num) cnt+=pre*base + suf + 1; else cnt+=(pre+1)*base; if(!num) cnt-=base; base*=10; } return cnt; } int main(){ std::ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); ll l,r; cin>>l>>r; cout<<f(r,2)-f(l-1,2)<<endl; }
这篇关于计数问题的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23Springboot应用的多环境打包入门
- 2024-11-23Springboot应用的生产发布入门教程
- 2024-11-23Python编程入门指南
- 2024-11-23Java创业入门:从零开始的编程之旅
- 2024-11-23Java创业入门:新手必读的Java编程与创业指南
- 2024-11-23Java对接阿里云智能语音服务入门详解
- 2024-11-23Java对接阿里云智能语音服务入门教程
- 2024-11-23JAVA对接阿里云智能语音服务入门教程
- 2024-11-23Java副业入门:初学者的简单教程
- 2024-11-23JAVA副业入门:初学者的实战指南