【POJ3696】The Luckiest Number-欧拉定理+快速幂

测试地址:The Luckiest Number
题目大意:给出一个正整数 L(2,000,000,000) ,要使正整数 888...8K 能整除 L ,求最小的K,如果不存在这样的 K 则输出0。
做法:这题的思路很神……没看题解的时候根本不知道怎么做,调也调了好一会,我好弱啊……
这一题需要使用欧拉定理+快速幂来解决。
我们发现999...9K可以表示为 10K1 ,所以 888...8K 可以表示为 8/9×(10K1) ,那么存在一个整数 p 使得8/9×(10K1)=L×p,所以 10K1=9/8×L×p 。由于等式右边要是整数,那么 p 应该满足8|(L×p)。因为 L 已经包含gcd(L,8)这些因子,所以 p 需要包含8/gcd(L,8)这些因子,所以可以将 p 写成p=8/gcd(L,8)×p1。将其代入前面的式子,可以得到 10K1=9×L/gcd(L,8)×p1 ,设 m=9×L/gcd(L,8) ,则原式可以写成 10K1=m×p1 ,这样我们就把问题转化成了求同余方程 10K1(modm) 的最小的正整数解。根据欧拉定理,可以得到 10φ(m)1(modm) ,可以证明如果 K 是一个小于φ(m)的解,那么 K|φ(m) 。那么我们就可以按照下列步骤完成这一题:
1.求出 m φ(m) φ(m) 的所有质因子。这一步可以用 O(N) 的试除法来做。
2.令 x=φ(m) ,对于 φ(m) 的每一个质因子 pi ,判断 10x/pi%m 是否等于 1 ,如果等于则令x=x/pi,否则跳过。由于指数很大,需要使用快速幂来算出结果,而因为数字直接乘起来可能会溢出,要用快速乘代替乘法。
3.对所有质因子进行以上操作后,最后的 x 就是最小的K
那么无解的情况呢?显然可知,如果 gcd(10,m)1 ,那么上面的同余方程就无解。这样我们就完美解决了这一题。
犯二的地方:试除法忘记探测最后剩余是不是1了,导致可能漏掉一个质因子,以后要注意……
以下是本人代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
using namespace std;
ll L,fac[1010]={0};

ll gcd(ll a,ll b)
{
  return (b==0)?a:gcd(b,a%b);
}

ll phi(ll x)
{
  ll p=x,s=x;
  for(ll i=2;i*i<=s;i++)
    if (!(x%i))
    {
      p=p/i*(i-1);
      while(!(x%i)) x/=i;
    }
  if (x>1) p=p/x*(x-1);
  return p;
}

void find_factor(ll x)
{
  ll s=x;
  fac[0]=0;
  for(ll i=2;i*i<=s;i++)
    if (!(x%i))
    {
      fac[++fac[0]]=i;
      while(!(x%i)) x/=i;
    }
  if (x>1) fac[++fac[0]]=x;
}

ll mult(ll a,ll b,ll mod)
{
  a%=mod,b%=mod;
  ll s=a,sum=0;
  while(b)
  {
    if (b&1)
    {
      sum+=s;
      if (sum>=mod) sum-=mod;
    }
    b>>=1;
    s<<=1;
    if (s>=mod) s-=mod;
  }
  return sum;
}

ll power(ll a,ll b,ll mod)
{
  ll s=a,sum=1;
  while(b)
  {
    if (b&1) sum=mult(sum,s,mod);
    b>>=1;s=mult(s,s,mod);
  }
  return sum;
}

int main()
{
  int t=0;
  while(scanf("%lld",&L)&&L)
  {
    t++;
    ll m=L/gcd(L,8)*9,p=phi(m),x=p;
    if (gcd(m,10)!=1) {printf("Case %d: 0\n",t);continue;}
    find_factor(p);
    for(int i=1;i<=fac[0];i++)
    {
      while(1)
      {
        x/=fac[i];
        if (power(10,x,m)!=1)
        {
          x*=fac[i];
          break;
        }
        else if (x%fac[i]) break;
      }
    }
    printf("Case %d: %lld\n",t,x);
  }

  return 0;
}
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值