关于数论、最幸运的数字

  AcWing 最幸运的数字

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

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

输入格式

输入包含多组测试用例。

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

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

输出格式

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

结果为 Case i:+一个整数 N,N代表满足条件的最小幸运数字的位数。如果满足条件的幸运数字不存在,则 N=0。

数据范围

1≤L≤2×109

输入样例:

8
11
16
0

输出样例:

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

   该题主要就是一个数学问题,让你找一个 x 个 8 组成的整数是 L 的倍数,并且保证找到 x 最小的可能,然后咱们首先分析一下该表达式。

       L | 88..8  ,该式子表示右边是左边的倍数。然后通过分析右边的数字。

=>  L  | 8 * (99..9  / 9)  =>  L  | 8/9 * (10^x -1)   这两部转换还是比较简单的,一个数值的计算。 

=> 9L | 8 * (10^x - 1)  左右同时乘以一个数,右边还是左边的倍数,所以可以将右边化简。

   设 d = gcd(9L , 8)   因为

=>  9L/d  |  8/d * (10^x-1)  两边同时除去 9L 和 8的最大公因数,那么gcd(9L/d,8/d)=1,两数互质。

=>  9L/d  |  8/d * (10^x-1)  你会发现,除去最大公因数之后,两数互质那么右边是左边的倍数跟 8/d 没有关系,可以直接省略。

=>  9L/d  |  10^x - 1  到这里 右边基本上到最简了

设 A = 9L/d

=>  A | 10x - 1  右边是左边的倍数,则可推出下式

=> 10^x  ≡ 1 (mod A)  会发现跟欧拉定理非常像

欧拉定理:对任意两个正整数 a, n,如果两者互质,那么 a^φ(n) ≡ 1( mod n)

    由定理可知,若 A 和 10 互质则有解,反之无解。

    但是x == φ(A)  并不能保证这个时候的 x 是最小的解,所以根据欧拉定理,只需要枚举 φ(A)的所有因数就行了。

  以上就是该题的核心思路,还涉及到一点问题。

欧拉函数的计算,因为只需要一个所以用的是 试除法 来计算的。

    然后就是一个快速幂,以及龟速乘。 因为幂次方太高,数据太大不能一次计算,但是可以取模,所以用到了快速幂,龟速乘跟快速幂的思想是一样的,因为数据在1e18之内,相乘有可能爆数据,这也是快速幂的常见错误之一,这个时候就需要一个龟速乘算法来弥补,用时间来换精度。

接下来就是代码了。

#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;
typedef long long ll;

ll gcd(ll a,ll b) // 非常基础的最大公约数模板
{
	return b?gcd(b,a%b):a;
}

ll get_euler(ll n) // 试除法 求 欧拉函数值模板
{
	ll s=n;
	for(ll i=2;i<=n/i;i++)
	{
		if(n%i==0)
		{
			while(n%i==0) n/=i;
			s = s / i * (i - 1);
		}
	}
	if(n>1) s=s/n*(n-1);
	return s;
}
ll quick_mul(ll a,ll b,ll c) // 龟速乘模板
{
	ll s=0;//注意初值为0,因为是累加。
	while(b)
	{
		if(b&1) s = (s + a)%c;
		a = (a+a)%c;
		b/=2;
	}
	return s;
}
ll quick_mi(ll a,ll b,ll c) //快速幂模板
{
	ll s=1;
	while(b)
	{
		if(b&1) s = quick_mul(s,a,c);
		a=quick_mul(a,a,c);
		b/=2;
	}
	return s;
}
int main(void)
{ 
	 ll n;
	 int T = 1;
	 while(cin>>n,n)
	 {
	 	ll mini = 1e18;

	 	ll d=gcd(n,8),a=9*n/d,phi=get_euler(a);
        // 分别对应的就是解析中的 d、A、φ(A)

	 	if(a%2==0||a%5==0) mini=0; // 如果不是互质,根据定理直接无解。
	 	else
	 	for(ll i=1;i<=phi/i;i++) // 枚举约数进行判断。
	 	    if(phi%i==0) // 因为因数都是一对一对的,找到其中一个就够了,
	 	    {
	 		    if(quick_mi(10,i,a)==1) mini=min(mini,i); //判断 10^i % a 等不等于1,并且更新mini值。
	 		    if(quick_mi(10,phi/i,a)==1) mini=min(mini,phi/i);//同上
		    }
	 	printf("Case %d: %lld\n", T ++ , mini);
	 	
	  } 
	return 0;
}


 
 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值