Kick Star 2018 Round B —— Problem A No Nine

   日期:2020-07-07     浏览:94    评论:0    
核心提示:目录题目描述输入输出Limits解题思路对于第一个条件的解决对于第二个条件的解决两个区间的划分代码描述题目描述  查数游戏,参与者不能说出被9整除或数位中含有9的数字。具体描述请点这里。输入  T:表示共有T个用例来进行测试  F,L:表示查数的区间 [F, L]  注:F和L都是可以被喊出的数字(即它们不被9整除,也不含有数字9)输出  每个测试用例输出一行 Case #x: y   ·x:第x个用例(从1开始)   ·y:该区间的合法数字个数(即不被九整除且不含9的数字的个数)Li

目录

  • 一、 题目描述
    • 1. 输入
    • 2. 输出
    • 3. Limits
  • 二、 解题思路
    • 1. 对于第一个条件的解决
    • 2. 对于第二个条件的解决
      • 2. 1 两个区间的划分
      • 2. 2 对整组部分的处理
      • 2. 3 对非整组部分的处理
  • 三、 代码描述

一、 题目描述

  查数游戏,参与者不能说出被9整除或数位中含有9的数字。

1. 输入

  T:表示共有T个用例来进行测试
  F,L:表示查数的区间 [F, L]
  注:F和L都是可以被喊出的数字(即它们不被9整除,也不含有数字9)

2. 输出

  每个测试用例输出一行 Case #x: y
   ·x:第x个用例(从1开始)
   ·y:该区间的合法数字个数(即不被九整除且不含9的数字的个数)

3. Limits

  • 1 ≤ T ≤ 100.
  • Time limit: 60 seconds per test set.
  • Memory limit: 1 GB.
  • F does not contain a 9 digit.
  • F is not divisible by 9.
  • L does not contain a 9 digit.
  • L is not divisible by 9.

Small dataset (Test set 1 - Visible)

  • 1 ≤ F < L ≤ 106.

Large dataset (Test set 2 - Hidden)

  • 1 ≤ F < L ≤ 1018.

二、 解题思路

  我们要计算从F到L之间有多少个合法数据,可以转换成求从0到X之间有多少个合法数据 n u m ( X ) num(X) num(X)。那么从F到L之间就有 n u m ( L ) − n u m ( F ) + 1 num(L) - num(F) + 1 num(L)num(F)+1个合法数据了。

  下面介绍求 n u m ( X ) num(X) num(X)的解法

  考虑一下不合法的数据,有两个条件:1) 不含数字9;2) 不能被9整除。

1. 对于第一个条件的解决

  既然数字中不能含有数字9,那我们把所有含有数字9的数去掉。剩下的数字仅由0~8这9位数字组成。
  我们将剩余的数字按顺序写出,如图1所示。剩下这些数字的排列完全等同于9进制数的排列(注:仍是十进制数,只是按照9进制排列以及进制转换可以计算该数的新位置)。

图1 除去含有数字9之后的数字

  

2. 对于第二个条件的解决

  去除所有含有数字9的数字以后,我们只需要考虑能否被9整除的问题了。
  对于数字X,我们写成如下形式
X = d n d n − 1 . . . d 0 X = d_{n}d_{n-1}...d_{0} X=dndn1...d0
  那数字X在上图1中的哪个位置呢?我们可以将它单纯地看成9进制数,那么9进制在上图中的位置自然可以通过将其转化成十进制数来得到:
Y = ∑ i = 0 n d i ⋅ 9 i Y = \sum_{i=0}^{n}d_{i}\cdot 9^{i} Y=i=0ndi9i
  那么数字X是图一示意的新数据中的第 (Y + 1) 个数。

  懒得看文字的推导可以直接跳到 2.1 两个区间的划分,图形的解释更容易理解。

  用组的方式来表述X的位置是:第 (9a+b) 个数。
(例如数字11在图中的第二行第二个数,那么11 = 9*1 +2, 代表:满1组之后的第二个数字。)

  那么现在有
Y + 1 = 9 a + b Y + 1 = 9a + b Y+1=9a+b

  观察图一,每一列的末位 d 0 d_{0} d0都是相等的,即b由 d 0 d_{0} d0决定:
b = ( d 0 + 1 ) % 9 b = (d_{0} +1) \% 9 b=(d0+1)%9

  所以当 d 0 = 8 d_{0} = 8 d0=8时,刚好满整组(看图一也能直接看出来)。对于整组部分,每组有8个合法数字(每行不合法的数字分别时0,18,27,36,45,54,63,72,81)。
  对于不整组的部分,我们遍历一下,检查每一个数字是否能被9整除。
  对于 X X X,它是第 Y + 1 Y+1 Y+1个数字,那么对于 X − d 0 X-d_{0} Xd0,它是第 Y − d 0 + 1 Y-d_{0}+1 Yd0+1个数字,剩下的从第 1 1 1 个数字到第 Y − d 0 Y-d_{0} Yd0 个数字恰好为整组部分。共有组数为:
∑ i = 1 n d i ⋅ 9 i − 1 \sum_{i=1}^{n}d_{i}\cdot 9^{i-1} i=1ndi9i1

2. 1 两个区间的划分

  按照上面的描述,我们将 [ 0 , X ] [0, X] [0,X]划为两个子区间
[ 0 , X − d 0 ) 和 [ X − d 0 , X ] [0, X-d_{0}) 和 [X-d_{0}, X] [0,Xd0)[Xd0,X]
  第一个区间为整组的区间,第二个区间为不正组的区间。
  如图2所示,对于数字134,分成两个区间红色部分和蓝色部分: [ 0 , 128 ] [0, 128] [0,128] [ 130 , 134 ] [130, 134] [130,134].

图2 数字134的位置及其分组情况

2. 2 对整组部分的处理

  共有多少整组呢。这完全可以由数字X除去最低位的其它位计算得到,即图3中绿色部分。组的数量a的计算:
a = ∑ i = 1 n d i ⋅ 9 i − 1 a = \sum_{i=1}^{n}d_{i}\cdot 9^{i-1} a=i=1ndi9i1
  而每一组(即图中的每一行)均有8个合法数字(每行为连续9个数字,必有一个是能被9整除的,故每行合法数据有8个)。故整组部分的合法数字有res1个:
r e s = 8 × a = 8 × ∑ i = 1 n d i ⋅ 9 i − 1 res = 8 × a =8 × \sum_{i=1}^{n}d_{i}\cdot 9^{i-1} res=8×a=8×i=1ndi9i1

2. 3 对非整组部分的处理

  非整组部分不超过8个数字,所以可以遍历一下,判断他们是否能被9整除.

  或者用 N % 9 > N % 10 N\%9 > N\%10 N%9>N%10 来判断数 X X X是否在图1中从对角线上方,若在从对角线1上方,则没有能被9整除的数,则最终结果+1;否则仅为 N % 10 N\%10 N%10。非整数部分中的合法数字个数为:
N % 10 + ( N % 9 > N % 10 ) N\%10 + (N\%9 > N\%10) N%10+(N%9>N%10)

  当然也可以用 N % 9 < = N % 10 N\%9 <= N\%10 N%9<=N%10 来判断数 X X X是否在图1中从对角线下面或者正好在从对角线上( X X X恰好能被9整除)。非整数部分中的合法数字个数为:
N % 10 + 1 − ( N % 9 < = N % 10 ) N\%10 + 1 - (N\%9 <= N\%10) N%10+1(N%9<=N%10)

三、 代码描述

#include <string>
#include <cmath>
#include <iostream>

using namespace std;

typedef long long ll;

ll solve(ll N){
    ll res = 0;
        
    string N_str = to_string(N);
    int len = N_str.length();
    
    // 计算整组数量
    for(int i = 0; i < len-1; i++){
        res = res*9 + (N_str[i]-48); 
    }
    res *= 8;
    
    // 非整组部分处理方法1——遍历
    for(ll i = N - N%10; i <= N; i++)
        if(i%9 != 0)
            res++;
            
    // 非整组部分处理方法2
    //res += N%10 + (N%9 > N%10);

    return res;
}

int main(){
    int T;
    ll F, L, res;
    cin >> T;
    for(int i = 1; i <= T; i++)
    {
        cin >> F >> L;
        res = solve(L) - solve(F) + 1;
        cout << "Case #" << i << ": " << res <<endl;
    }
    
    return 0;
}
  1. 说从对角线只是一个描述。对于整张由不含数字9的数字构成的新表而言,其十进制模9的值为0的位置,在表上有从右上到左下的趋势。这点很好理解,相邻的9的倍数末位数字依次减1,如:27,36,45……末位数字位7,6,5,而十位数字依次+1。当找到207为9的倍数时,也很容易得到216,225……为9的倍数,在表中是有一个从右上到左下的趋势的。 ↩︎

 
打赏
 本文转载自:网络 
所有权利归属于原作者,如文章来源标示错误或侵犯了您的权利请联系微信13520258486
更多>最近资讯中心
更多>最新资讯中心
0相关评论

推荐图文
推荐资讯中心
点击排行
最新信息
新手指南
采购商服务
供应商服务
交易安全
关注我们
手机网站:
新浪微博:
微信关注:

13520258486

周一至周五 9:00-18:00
(其他时间联系在线客服)

24小时在线客服