ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

计数问题

2022-02-07 04:31:08  阅读:155  来源: 互联网

标签:数字 ll overline leq cdots num 计数问题


计数问题

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)

  1. \(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$$
  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}}$$时,若 \(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$$
  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;
}

标签:数字,ll,overline,leq,cdots,num,计数问题
来源: https://www.cnblogs.com/BlankYang/p/15865161.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有