HDU 2815 Mod Tree 解a^x = b(mod n)非互质

题意:

求模方程 a^x = b(mod n)是否存在解x,如果存在,输出最小的x。

解题思路:

首先考虑简单的情况,求a^x = b(mod n),如果a,n互质,由欧拉定理 a^( phi(n) ) = 1 (mod n)可知,当x>phi(n)后a^x就开始循环了。所以只需要检查前phi(n)项是否有解,因为phi(n) <= n,为了方便,统一设定检查范围为0到n。然后我们先检查前m项 (令m = sqrt(n) ),保存所有a^i mod n的值 (0 <= i < m),接下来对于a^(m),a^(m+1)...a^(2m+1)就不用枚举来检查了,如果他们当中有解,则相当于存在a^i* a^m = b(mod n)。两边左乘a^(-m)得 a^i = ( b*a^(-m) ) (mod n),所以只需要判断有没有a^i = b*a^(-m) 。

如果a,n不互质的话,直接这样子做就不行了,既然我们有了a,n互质的做法,那就要想办法使得a,n互质。

所以每次都要把a,n的gcd约去,同时b也要约去gcd,如果b不能约去gcd说明无解。这样假设需要cnt个a和n约去才能使得a,n互质,这cnt个a约后得到的设为now,结果就是求now*a^x = b (mod n),也就转化成a,n互质的情况了,这样就很简单了~贴个代码 (ps:貌似好久没写博客了)


#include <stdio.h>
#include <math.h>
#include <map>
using namespace std;
#define LL __int64

int exgcd(int a, int b, int &x, int &y) {
    if(!b) {
        x = 1; y = 0;
        return a;
    }
    int ret = exgcd(b, a%b, y, x);
    y -= a/b*x;
    return ret;
}

int gcd(int a, int b) {
    return b ? gcd(b, a%b) : a;
}
// 求逆元
int inv(int a, int n) {
    int x, y;
    int d = exgcd(a, n, x, y);
    if(x < 0)   x += n;
    return x;
}

int mul_mod(LL a, LL b, int n) {
    return a * b % n;
}

int pow_mod(int x, int n, int m) {
    int ret = 1;
    while(n) {
        if(n&1) ret = (LL)ret*x%m;
        x = (LL)x*x%m;
        n /= 2;
    }
    return ret;
}

//求解a^x = b (mod n) a, n互质
int log_mod(int a, int b, int n) {
    int m = sqrt(n+0.5);
    int v = inv(pow_mod(a, m, n), n);
    map<int,int> x;
    x[1] = 0;
    int e = 1;
    for(int i = 1;i < m; i++) {
        e = mul_mod(e, a, n);
        if(!x.count(e)) x[e] = i;
    }
    for(int i = 0;i < m; i++) {
        if(x.count(b))  return i*m + x[b];
        b = mul_mod(b, v, n);
    }
    return -1;
}

int solve(int a, int p, int b) {
    int cnt = 0;
    int now = 1;
    while(true) {
        // 结果已经等于b的情况
        if(now%p == b)  return cnt;
        int d = gcd(a, p);
        if(d == 1) {
            int v = inv(now, p);
            int ret = log_mod(a, (LL)b*v%p, p);
            if(ret == -1)   return -1;
            return ret + cnt;
        }
        if(b % d)   return -1;
        b /= d;
        p /= d;
        now = (LL)now*(a/d)%p;
        cnt++;
    }
}

int main() {
    int a, p, b;
    while(scanf("%d%d%d", &a, &p, &b) != -1) {
        if(b >= p) {
            puts("Orz,I can’t find D!"); continue;
        }
        int ans = solve(a, p, b);
        if(ans == -1)   puts("Orz,I can’t find D!");
        else    printf("%d\n", ans); 
    }
    return 0;
}


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值