最幸运的数字

最幸运的数字

8 是中国的幸运数字,如果一个数字的每一位都由 8 构成则该数字被称作是幸运数字。

现在给定一个正整数 L,请问至少多少个 8 连在一起组成的正整数(即最小幸运数字)是 L 的倍数。

输入格式

输入包含多组测试用例。

每组测试用例占一行,包含一个整数 L

当输入用例 L=0 时,表示输入终止,该用例无需处理。

输出格式

每组测试用例输出结果占一行。

结果为 Case i: +一个整数 NN 代表满足条件的最小幸运数字的位数。

如果满足条件的幸运数字不存在,则 N=0

数据范围

1L2×109

输入样例:

8
11
16
0

输出样例:

Case 1: 1
Case 2: 2
Case 3: 0

 

解题思路

  假设最小位数为x,那么

     88x=8×11x=8×99x9=8×10x19

  因此问题由求最小的x满足L88x等价变成了求最小的x满足L8×10x19

      L88xL8×10x199L8×(10x1)9Lgcd(9L,8)8gcd(9L,8)×(10x1)9Lgcd(9L,8)(10x1)C(10x1)      note:  C:=9Lgcd(9L,8)10x1(modC)

  对于10x1(modC),由欧拉定理,如果gcd(a,n)=1aφ(n)1(modn),如果gcd(10,C)1那么无解(可以由裴蜀定理来证明),否则必然有解x可以取φ(C)。但题目要求最小的xφ(C)虽然满足这个同余式但不一定是最小的x

  实际上还有一个结论:所有满足同余方程10x1(modC)的最小的正整数x一定满足xφ(C)。因此可以对φ(C)进行约数分解,把所有约数带到这个同余式找到满足方程的最小的约数。

  更一般的,10可以换成任意与C互质的数a,那么所有满足同余方程ax1(modC)的最小的正整数x一定满足xφ(C)

  下面证明这个结论。假设x是满足同余方程的最小值且xφ(C),那么φ(C)一定可以表示成φ(C)=kx+r (0<r<x)。由于ax1(modC),因此akx1(modC),所以

aφ(C)1(modC)akx+r1(modC)      ar1(modC)

  因此这样就找到一个新的r (0<r<x)使得ar1(modC),且rx更小,就矛盾了。

  总结一下这题的流程步骤,先求出C=9Lgcd(9L,8),然后判断10C是否互质,如果不互质那就无解。否则求出φ(C),对φ(C)分解约数并通过快速幂求满足10x1(modC)的最小约数。

  还有一个地方需要注意的是,由于C最大可以取到1.8×1010(大概),因此在快速幂中两个数相乘是可能爆long long的,因此还要写个龟速乘来求两个数的乘积。

  AC代码如下:

复制代码
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long LL;
 5 
 6 LL gcd(LL a, LL b) {
 7     return b ? gcd(b, a % b) : a;
 8 }
 9 
10 LL phi(LL x) {
11     LL ret = x;
12     for (int i = 2; i <= x / i; i++) {
13         if (x % i == 0) {
14             ret -= ret / i;
15             while (x % i == 0) {
16                 x /= i;
17             }
18         }
19     }
20     if (x > 1) ret -= ret / x;
21     return ret;
22 }
23 
24 LL qmul(LL a, LL k, LL p) {
25     LL ret = 0;
26     while (k) {
27         if (k & 1) ret = (ret + a) % p;
28         a = (a + a) % p;
29         k >>= 1;
30     }
31     return ret;
32 }
33 
34 LL qmi(LL a, LL k, LL p) {
35     LL ret = 1;
36     while (k) {
37         if (k & 1) ret = qmul(ret, a, p);   // ret*a可能会爆long long,因此用龟速乘
38         a = qmul(a, a, p);  // 同理
39         k >>= 1;
40     }
41     return ret;
42 }
43 
44 int main() {
45     int tot = 1;
46     LL l;
47     while (scanf("%lld", &l), l) {
48         LL c = 9 * l / gcd(9 * l, 8), ret = 1e18;
49         if (gcd(10, c) != 1) {  // 10和c不互质,无解
50             ret = 0;
51         }
52         else {
53             LL phi_c = phi(c);
54             for (int i = 1; i <= phi_c / i; i++) {  // 分解phi(c)的约数
55                 if (phi_c % i == 0) {
56                     if (qmi(10, i, c) == 1) ret = min(ret, (LL)i);  // 约数满足同余式则求最小的约数
57                     if (qmi(10, phi_c / i, c) == 1) ret = min(ret, phi_c / i);  // 另外一个约数
58                 }
59             }
60         }
61         printf("Case %d: %lld\n", tot++, ret);
62     }
63     
64     return 0;
65 }
复制代码

 

参考资料

  AcWing 202. 最幸运的数字(算法提高课):https://www.acwing.com/video/705/

posted @   onlyblues  阅读(98)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
历史上的今天:
2022-01-28 圆形牛棚
Web Analytics
点击右上角即可分享
微信分享提示