《算法竞赛进阶指南》0x33同余 POJ3696 The Luckies Number

题目链接:http://poj.org/problem?id=3696

题目要求求最短的由连续的8构成的数使得这个数是L的倍数,我们首先求出phi(mod)可以证明a^x%mod=1的解x最大是phi(mod)而且其他解都是phi(mod)的约数,phi(mod)的约数大约是log(phi(mod))个。首先通过O(sqrt(mod))时间复杂度将mod分解质因数并计算phi(mod),他的约数个数最多是sqrt(phi(mod)),通过枚举即可。

注意需要通过ll求指数进行验证,所以需要通过加强的ll乘法对快速幂中的乘法进行优化。不然会WA,加强后的快速幂大约时间复杂度是O(logn^2)

证明:

 

 

 

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const ll inf=1e18;
ll L;
ll phi(ll mod){
    ll ans=mod;
    for(ll i=2;i*i<=mod;i++){
        if(mod%i)continue;
        ans=ans/i*(i-1);
        while(!(mod%i))mod/=i;
    }
    if(mod>1)ans=ans/mod*(mod-1);
    return ans;
}
ll ksc(ll n,ll k,ll mod){//n*k mod mod 
    ll ans=0;
    while(k){
        if(k&1)ans=(ans+n)%mod;
        k>>=1;
        n=(n*2)%mod;
    }
    return ans;
}
ll pow(ll n,ll k,ll mod){
    ll ans=1;
    n%=mod;
    while(k){
        if(k&1)ans=ksc(ans,n,mod);//ll防止溢出 
        n=ksc(n,n,mod);
        k>>=1;
    }
    return ans;
}
ll gcd(ll x,ll y){
    return y?gcd(y,x%y):x;
}
int main(){
    int T=0;
    while(scanf("%lld",&L) && L){
        ll mod=    L/gcd(L,8ll)*9ll;
        ll xmax=phi(mod);    
        ll ans=inf;
        for(ll i=1;i*i<=xmax;i++){//枚举phi(mod)的所有的约数,除了开根的都是成对出现的 
            if(xmax%i)continue;
            if(pow(10,i,mod)==1)ans=min(ans,i);
            if(i!=xmax/i && pow(10,xmax/i,mod)==1)ans=min(ans,xmax/i);
        }
        
        printf("Case %d: ",++T);
        if(ans==inf)printf("0\n");
        else printf("%lld\n",ans);
    }
    return 0;
}

 

posted @ 2020-06-23 10:04  WA自动机~  阅读(188)  评论(0编辑  收藏  举报