(至少要几个8才能整除给定的数)POJ 3696 The Luckiest number 欧拉函数 或 BSGS

本文介绍了解决POJ3696问题的两种方法:BSGS算法和欧拉定理,并提供了详细的代码实现。BSGS算法通过预处理和查找表的方式有效地解决了大数幂等方程的求解问题;而欧拉定理则利用数论中的性质简化了计算过程。
摘要由CSDN通过智能技术生成

资料来源:https://blog.csdn.net/danliwoo/article/details/48865127

原题见POJ 3696

(不得不说,这哥们的公式是真的6,尝试过手敲之后果断放弃,选择截屏,这大概就是蒟蒻和大佬的差别吧。。) 

 

BSGS

很快想起了BSGS有木有!上篇博客已经讲过这种算法。

/*--------------------------------------------
 * File Name: POJ 3696
 * Author: Danliwoo
 * Mail: Danliwoo@outlook.com
 * Created Time: 2015-10-02 22:16:59
--------------------------------------------*/

#include <cstdio>
#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
#include <cmath>
using namespace std;
#define N 44725
#define LL long long
#define MOD 150000   //是sqrt(1999999999*9)要求那么大的
LL gcd(LL a, LL b)
{
    return b == 0?a:gcd(b, a%b);
}
LL hs[MOD],head[MOD],next[MOD],id[MOD],top;
void insert(LL x, LL y)
{
    LL k = x%MOD;
    hs[top] = x, id[top] = y, next[top] = head[k], head[k] = top++;
}
LL find(LL x)
{
    LL k = x%MOD;
    for(LL i = head[k]; i != -1; i = next[i])
        if(hs[i] == x)
            return id[i];
    return -1;
}
LL mult(LL a, LL b, LL c)
{
    a %= c; b %= c;
    LL ret = 0, tmp = a;
    while(b)
    {
        if(b & 1LL)
        {
            ret += tmp;
            if(ret > c) ret -= c;
        }
        tmp <<= 1;
        if(tmp > c) tmp -= c;
        b >>= 1;
    }
    return ret%c;
}
LL BSGS(LL a,LL b,LL c)
{
    memset(head, -1, sizeof(head));
    top = 1;
    LL m = sqrt(c*1.0), j;
    long long x = 1, p = 1;
    for(LL i = 0; i < m; ++i, p = p*a%c)
        insert(p*b%c, i);//存的是(a^j*b, j)
    for(long long i = m; ;i += m)
    {
        if( (j = find(x = mult(x,p,c))) != -1 )//注意x要用mult()函数否则超LL
            return i-j;  //a^(ms-j)=b(mod c)
        if(i > c)
            break;
    }
    return 0;
}
int main()
{
    LL o = 0;
    LL n;
    while(scanf("%lld", &n), n)
    {
        printf("Case %d: ", ++o);
        LL m = n/gcd(n, 8)*9;
        if(gcd(m,10) != 1)
        {
            printf("0\n");
            continue;
        }
        printf("%lld\n", BSGS(10,1,m));
    }
    return 0;
}

Euler

还可以试一试欧拉定理。

/*--------------------------------------------
 * File Name: POJ 3696
 * Author: Danliwoo
 * Mail: Danliwoo@outlook.com
 * Created Time: 2015-10-02 14:26:08
--------------------------------------------*/

#include <cstdio>
#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
#include <cmath>
using namespace std;
#define N 44725
#define LL long long
LL num[N], prim[N];
int cnt = 0;
void get_prim()
{
    for(int i = 0;i < N;i++) num[i] = i;
    for(int i = 2;i < N;i++) if(num[i])
    {
        for(int j = i*i;j < N;j += i)
            num[j] = 0;
        prim[cnt++] = i;
    }
}
LL phi(LL n)
{
    LL ans = n;
    for(int i = 0;prim[i]*prim[i]<=n;i++) if(n%prim[i] == 0)
    {
        ans -= ans/prim[i];
        while(n%prim[i] == 0) n /= prim[i];
    }
    if(n != 1)
        ans -= ans/n;
    return ans;
}
LL gcd(LL a, LL b)
{
    return b == 0?a:gcd(b, a%b);
}
LL mult(LL a, LL b, LL c)
{
    a %= c; b %= c;
    LL ret = 0, tmp = a;
    while(b)
    {
        if(b & 1LL)
        {
            ret += tmp;
            if(ret > c) ret -= c;
        }
        tmp <<= 1;
        if(tmp > c) tmp -= c;
        b >>= 1;
    }
    return ret%c;
}
LL po(LL a, LL k, LL m)
{
    if(k == 0) return 1;
    if(k == 1) return a%m;
    LL t = po(a, k/2, m);
    t = mult(t, t, m);
    if(k & 1LL) t *= a;
    return t%m;
}
LL solve(LL x, LL p, LL m, LL f)
{
    LL d = po(10, x, m);
    if(d == 1)
    {
        if(x%p == 0)
            return solve(x/p, p, m, f+1);
        else
            return x;
    }
    if(f == 0) return x;
    return x*p;
}
int main()
{
    get_prim();
    int o = 0;
    LL n;
    while(scanf("%lld", &n), n)
    {
        printf("Case %d: ", ++o);
        LL m = n/gcd(n, 8)*9;
        if(gcd(m,10) != 1)
        {
            printf("0\n");
            continue;
        }
        if(po(10, 1, m) == 1)
        {
            printf("1\n");
            continue;
        }
        LL x = phi(m);
        LL ans = x, t = x;
        for(int i = 0; prim[i]*prim[i] <= x && i < cnt;i++) if(ans%prim[i] == 0)
        {
            while(t % prim[i] == 0) t /= prim[i];
            ans = solve(ans, prim[i], m, 0);
        }
        if(t != 1)
            ans = solve(ans, t, m, 0);
        ans = ans == x ? 0 : ans;
        printf("%lld\n", ans);
    }
    return 0;
}

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值