poj 3696 The Luckiest Number

时间:2024-01-17 19:12:38

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=;
a%=mod;
b%=mod;
while(b)
{
if(b&) ans=(ans+a)%mod;
b>>=;
a=(a+a)%mod;
}
return ans;
}
ll quick_power(ll a,ll b,ll mod)//这题不爆longlong,但是这样是必须的,因为9*n在longlong范围内
{
ll ans=;
a%=mod;
while(b)
{
if(b&) ans=quick_multiply(ans,a,mod);
b>>=;
a=quick_multiply(a,a,mod);
}
return ans;
}
int main()
{
ll n;
ll cnt=;
while()
{
scanf("%lld",&n);
if(n==) return ;
printf("Case %lld: ",++cnt);
n=*n/gcd(n,);
ll m=n;
ll phi=n;
if(gcd(n,)!=)//这是欧拉定理所必须满足的,如果不行显然无解
{
printf("0\n");
continue;
}
for(ll i=;i*i<=m;++i)
{
if(m%i==)
{
phi=phi/i*(i-);
while(m%i==)
{
m/=i;
}
}
}
if(m!=) phi=phi/m*(m-);
// cout<<"phi="<<phi<<endl;调试信息
ll minn=phi;//我想取最小值,且最大值是phi
for(ll i=;i*i<=phi;i++)//这步是验证。
{
if(phi%i==)
{
if(quick_power(,i,n)==) minn=min(minn,i);
if(quick_power(,phi/i,n)==) minn=min(minn,phi/i);
}
}
printf("%lld\n",minn);
}
}

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

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