【POJ 3696】The Luckiest Number

本文探讨了如何计算由连续的8组成的数成为特定数值l的倍数所需的最少个数。通过数学推导,将问题转化为求解模运算下的指数问题,并提供了完整的算法实现,包括快速幂和模运算等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

至少多少个 88 连在一起组成的数是 l(l2×109) 的倍数。

算法分析

xx8 连在一起组成的数是 ll 的倍数,则表示为 l|8×(10x1)9

转化一下就是 9×l|8×(10x1)9×l|8×(10x−1),接下来怎么转化?

9×l9×l10x110x−1 的约数吗?不一定,还需要从重去除 ll8 分解质因数后的公共部分,即 gcd(8,l)gcd(8,l)

所以上式就变成 9×lgcd(8,l)|10x19×lgcd(8,l)|10x−1,即 10x1(mod9×lgcd(8,l))10x≡1(mod9×lgcd(8,l))

有一个结论:

  • 10x1(modp)10x≡1(modp),满足条件的最小的 xx 一定是 ϕ(p) 的约数。(反证法)

然后就可以求出 ϕ(9×lgcd(8,l))ϕ(9×lgcd(8,l)) 的值,枚举它的所有约数,使用快速幂一一判断是否满足条件即可。

注意开 6464 位整数,计算快速幂的时候中间结果会超过 6464 位整数范围,因此需要利用之前本博客中介绍的技巧计算。

代码实现

#include <cstdio>
#include <climits>
#include <algorithm>
typedef long long int ll;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
inline ll eular(ll n) {
    ll ans=n;
    for(ll i=2;i*i<=n;++i) {
        if(n%i==0) {
            ans=ans/i*(i-1);
            while(n%i==0) n/=i;
        }
    }
    if(n>1) ans=ans/n*(n-1);
    return ans;
}
ll p;
ll mul(ll a,ll b) {
    ll temp=(long double)a*b/p;
    ll ans=a*b-temp*p;
    if(ans>=p) ans-=p;
    if(ans<0) ans+=p;
    return ans;
}
ll pow(ll n,ll k) {
    ll ans=1;
    while(k) {
        if(k&1) ans=mul(ans,n);
        n=mul(n,n);k>>=1;
    }
    return ans;
}
int main() {
    ll l;int kase=0;
    while(scanf("%lld",&l)==1&&l) {
        p=l*9/gcd(8,l);ll n=eular(p),ans=1LL<<60;
        for(ll i=1;i*i<=n;++i) {
            if(n%i==0) {
                if(pow(10,i)==1) {ans=i;break;}
                else if(i*i!=n&&pow(10,n/i)==1) ans=std::min(ans,n/i);
            }
        }
        printf("Case %d: %lld\n",++kase,ans==1LL<<60?0:ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值