Codeforces 10C Digital Root

知识点:计数原理、数学推理、反向思考

题目

Not long ago Billy came across such a problem, where there were given three natural numbers A, B and C from the range [1, N], and it was asked to check whether the equation AB = C is correct. Recently Billy studied the concept of a digital root of a number. We should remind you that a digital root d(x) of the number x is the sum s(x) of all the digits of this number, if s(x) ≤ 9, otherwise it is d(s(x)). For example, a digital root of the number 6543 is calculated as follows: d(6543) = d(6 + 5 + 4 + 3) = d(18) = 9. Billy has counted that the digital root of a product of numbers is equal to the digital root of the product of the factors’ digital roots, i.e. d(xy) = d(d(x)d(y)). And the following solution to the problem came to his mind: to calculate the digital roots and check if this condition is met. However, Billy has doubts that this condition is sufficient. That’s why he asks you to find out the amount of test examples for the given problem such that the algorithm proposed by Billy makes mistakes.

输入

The first line contains the only number N (1 ≤ N ≤ 106).

输出

Output one number — the amount of required A, B and C from the range [1, N].

样例

输入1

4

输出1

2

输入2

5

输出2

6

提示

For the first sample the required triples are (3, 4, 3) and (4, 3, 3).

题意

Digital Root的递归定义为:若一个数字的各位数字之和小于等于9则为各位数字之和,否则为各位数字之和的Digital Root,记为d(x),且有d(xy)=d(d(x)d(y)),即乘积的Digital Root等于Digital Root的乘积的Digital Root。有人认为只要d©=d(d(A)d(B))就可以判断C=AB,其中A,B,C均为正整数且不超过N。存在A,B,C的不同组合,使得这个组合能反驳这个观点,要求输出组合数量。

思路

  1. Digital Root通俗的理解为,只要一个数字的位数大于1位就将各位数字相加得到新的数,重复操作得到的1位数就是Digital Root。
  2. 反驳观点的方法为,找到一对A,B,C使得AB≠C,但d(C)=d(d(A)d(B))。
  3. 正向考虑,发现AB≠C的组合太多,若使用三重循环分别枚举A,B,C,然后求d(A),d(B),d(C)判断d(C)=d(d(A)d(B)),时间复杂度为O(n^3),会超时。
  4. 反向考虑,所有能反驳观点的组合中,必满足d(C)=d(d(A)d(B)),但AB≠C。而根据通俗理解可以知道等号左右取值范围只能是0~9,但对0~9中任意一个数字(设为e)而言,等号左右等于e的组合有很多,根据分步计数原理,为满足d(C)=e的C的个数乘以满足d(d(A)d(B))=e的A的个数乘满足d(d(A)d(B))=e的B的个数,分类计数原理得总数为e取0~9时满足上述条件的A,B,C组合个数的和。再从得到的和中减去满足AB=C的组合。
  5. 分析满足d(C)=d(d(A)d(B))的组合个数。考虑到A,B,C都小于N,可以枚举1~N的所有值,并计算值的Digital Root,就可以统计Digital Root为0~9的数字分别为有多少,从而在后面的处理使用O(1)得到d(x)=e时x的取值个数,x取A,B,C即可得到A,B,C的取值个数。
  6. 通过d(C)=e计算C的个数可直接用上述方法。为了通过d(d(A)d(B))=e计算A,B的组合个数,可枚举d(A),d(B)(属于0~9),如果d(d(A)d(B))=e则把d(A),d(B)的A,B的取值个数相乘再乘以之前算出的d(C)的C的个数。当然因为d(d(A)d(B))算出的值可用作得到d(C)的个数,故可以直接把d(A),d(B),d(d(A)d(B))的A,B,C的取值个数相乘。
  7. 分析AB=C的组合个数。AB=C<=N,所以B<=N/A,即B取值为1~N/A,可枚举A从1~N,N/A为B的取值个数,而C可由A,B计算得到,A,B确定时,C的取值只有一个,所以组合个数为A从1~N时B的取值个数的和。
  8. 分析d(x)的计算方法,可直接使用定义计算,另一种计算方法需要证明:
    当x%9==0时,d(x)=9;
    当x%9!=0时,d(x)=x%9.
    证明如下:
    x==9时,d(x)=9
    x<9时,d(x)=x%9
    x>9时,
    设x=x0+x1*10+x2*100+…+xn*1000…
    =x0+x1+x2+…+xn+x1*9+x2*99+…+xn*999…
    =d(x)+9*(x1*1+x2*11+…+xn*111…)
    d(x)=x%9
    证毕。
    (数论杀我)
    实现思路见代码。

代码

//克服WA难,终得AC
#include"bits/stdc++.h"
#define ll long long
#define rep(i,a,b) for(ll i=a;i<b;i++)
#define reb(i,a,b) for(ll i=a;i<=b;i++)
#define rev(i,a,b) for(ll i=a-1;i>=b;i--)
#define red(i,a,b) for(ll i=a;i>=b;i--)
#define clr(arr,val) memset(arr,val,sizeof arr)
using namespace std;

ll cnt[20];//1~N中d(x)=i的个数

ll d(ll x) {
   
  return x%9?x%9:9;
}

int main() {
   
  ll n;
  cin>>n;
  //枚举1~N,对d(i)出现次数计数
  reb(i,1,n) cnt[d(i)]++;
  ll ans=0;
  //计算d(C)=d(d(A)d(B))的个数。枚举d(A)、d(B),cnt[i]为A的个数,cnt[j]为B的个数,cnt[d(i*j)]为通过d(A)和d(B)计算出的C的个数。
  reb(i,0,9) reb(j,0,9) ans+=cnt[i]*cnt[j]*cnt[d(i*j)];
  //计算AB=C的个数。枚举A,计算B的个数,从答案中减去
  reb(i,1,n) ans-=n/i;
  cout<<ans<<endl;
  return 0;
}
全部评论

相关推荐

02-07 12:06
已编辑
华侨大学 测试开发
最近看到很多&nbsp;92&nbsp;的,甚至是硕士,开始往测开赛道卷,说实话有点看不懂。先把话说清楚,大厂里的测开,绝大多数时间干的还是测试的活,只是写点自动化脚本、维护测试平台、接接流水线,真正像开发一样做系统、做架构、做核心平台的测开少得可怜,基本都集中在核心提效组,而且人很少,外面进去的大概率轮不到你,我想真正干过人都清楚。很多人被洗脑了,以为测开也是开,和后端差不多,只是更简单、更轻松、还高薪。现实情况是,测开和开发的职业路径完全不一样。开发的核心是业务和系统能力,测开的核心是稳定性和覆盖率,前者是往上走,后者天花板非常明显。你可以见到很多开发转测开,但你很少见到干了几年测开还能顺利转回开发的。更现实一点说,92&nbsp;的高学历如果拿来做测开,大部分时间就是在做重复性很强的杂活,这种工作对个人能力的放大效应非常弱。三年下来,你和一个双非的,甚至本科的测开差距不会太大,但你和同龄的后端、平台开发差距会非常明显。这不是努不努力的问题,是赛道问题。所谓测开简单高薪,本质上是把极少数核心测开的上限,当成了整个岗位的常态来宣传。那些工资高、技术强的测开,本身就是开发水平,只是挂了个测开的名。普通人进去,99%&nbsp;做的都是项目兜底型工作,而不是你想象中的平台开发。测开不是不能做,但它绝对不是开发的平替,也不是性价比最优解。如果你是真的不想做开发,追求稳定,那测开没问题。但如果你只是觉得测开比后端容易,还能进大厂,那我劝你冷静一点,这只是在用短期安全感换长期天花板。有92的学历,如果你连测开这些重复性工作都能心甘情愿接受,那你把时间精力用在真正的开发、系统、业务深度上,回报大概率比卷测开要高得多。想清楚再下场,别被岗位名和话术带偏了,就算去个前端客户端也是随便占坑的,测开是一个坑位很少赛道,反而大面积学历下放,不用想也能知道会是什么结果,我想各位在JAVA那里已经看到了
小浪_Coding:工作只是谋生的手段 而不是相互比较和歧视
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务