1. 程式人生 > >poj 3696 The Luckiest Number

poj 3696 The Luckiest Number

!= 一個 poj phi 一個數 tin 這一 ans cst

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.

poj 3696 The Luckiest Number