Kick Start 2018 Round B - Problem A No Nine

一、 题目描述

  查数游戏,参与者不能说出被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  剩余数字

图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 <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的倍数,在表中是有一个从右上到左下的趋势的。 ↩︎

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值