poj 3696 The Luckiest Number

The Luckiest Number

    题目大意:给你一个int范围内的正整数n,求这样的最小的x,使得:连续的x个8可以被n整除。

    注释:如果无解输出0。poj多组数据,第i组数据前面加上Case i: 即可。

      想法:这题还是挺好的。我最开始的想法是一定有超级多的数输出0。然后...我就在哪里找啊找....其实这道题是一道比较好玩儿的数论题。我们思考:连续的8可用什么来表示出来?$\frac{(10^x-1)}{9}\cdot 8$。其实想到这一步这题就做完了。这题的精髓就在于告诉我们连续的连续的一串数的表达方式。想到这点其实有一个比较容易接受的方法:这鬼东西是一个等比数列。然后,式子就可以化成了以下的形式及推导

    $\Rightarrow n|\frac{10^x-1}{9}\cdot 8$

    $\Rightarrow 9\cdot n|(10^x-1)\cdot 8$

    $\Rightarrow \frac{9\cdot n}{gcd(n,8)}|\frac{(10^x-1)\cdot8}{gcd(n,8)}$

    $\because gcd(\frac n{gcd(n,8)},\frac8{gcd(n,8)})=1$

    且$gcd(9,8)=1$

    $\therefore gcd(\frac{9\cdot n}{gcd(n,8)},\frac{8}{gcd(n,8)})=1$

    $\Rightarrow \frac{9\cdot n}{gcd(n,8)}|10^x-1$

    $\Rightarrow 10^x\equiv1(mod\frac{9\cdot n}{gcd(n,8)})$

    所以此时,我们只需要枚举mod数即可。但是有些操作是不必要的,在此,我们有两种简单的优化:

    1.对于mod数取$\varphi$,然后暴力枚举$\varphi$的所有因子。时间复杂度$O(\sqrt{n})$,验证是用快速幂,时间复杂度O(logn),所以,总时间复杂$O(\sqrt{n}\cdot {logn})$。

    2.用BSGS优化,我没想到(鸣谢CQzhangyu)。时间复杂度同理。

    但是对于第一种我们可以用Miller_Rabin 和Pullard_rho进行爆炸般的优化,但是没什么必要......

      最后,附上丑陋的代码......

#include <iostream>
#include <cstdio>
typedef long long ll;
using namespace std;
ll gcd(ll a,ll b)//只取一次mod的gcd,鸣谢EdwardFrog
{
    return b?gcd(b,a%b):a;
}
ll quick_multiply(ll a,ll b,ll mod)//快速乘,防止爆longlong,虽然没有必要
{
    ll ans=0;
    a%=mod;
    b%=mod;
    while(b)
    {
        if(b&1) ans=(ans+a)%mod;
        b>>=1;
        a=(a+a)%mod;
    }
    return ans;
}
ll quick_power(ll a,ll b,ll mod)//这题不爆longlong,但是这样是必须的,因为9*n在longlong范围内
{
    ll ans=1;
    a%=mod;
    while(b)
    {
        if(b&1) ans=quick_multiply(ans,a,mod);
        b>>=1;
        a=quick_multiply(a,a,mod);
    }
    return ans;
}
int main()
{
    ll n;
    ll cnt=0;
    while(1)
    {
        scanf("%lld",&n);
        if(n==0) return 0;
        printf("Case %lld: ",++cnt);
        n=9*n/gcd(n,8);
        ll m=n;
        ll phi=n;
        if(gcd(n,10)!=1)//这是欧拉定理所必须满足的,如果不行显然无解
        {
            printf("0\n");
            continue;
        }
        for(ll i=2;i*i<=m;++i)
        {
            if(m%i==0)
            {
                phi=phi/i*(i-1);
                while(m%i==0)
                {
                    m/=i;
                }
            }
        }
        if(m!=1) phi=phi/m*(m-1);
        // cout<<"phi="<<phi<<endl;调试信息
        ll minn=phi;//我想取最小值,且最大值是phi
        for(ll i=1;i*i<=phi;i++)//这步是验证。
        {
            if(phi%i==0)
            {
                if(quick_power(10,i,n)==1) minn=min(minn,i);
                if(quick_power(10,phi/i,n)==1) minn=min(minn,phi/i);
            }
        }
        printf("%lld\n",minn);
    }
}

 

    小结:错误,枚举一个数的因子其实是可以根号时间内完成的...我傻逼了......

        还有,别忘记phi开始的初值是n,不是1.

 

posted @ 2018-01-03 09:41  JZYshuraK_彧  阅读(457)  评论(0编辑  收藏  举报