a^b === c (mod p)知二求一: p已知

知a b求c

求x满足 abx(modp) , 即求 x=abmodp

快速幂。。。

LL pow_mod(LL a, LL b, LL p) {
    LL r = 1; a %= p;
    while(b) {
        if(b&1) r = (r*a) % p;
        a = (a*a) % p;
        b >>= 1;
    }
    return r;
}

知a c求b

求x满足

axc(modp)

使用BSGS算法即可 =>Baby Step Giant Step(好奇怪的名字)及其扩展: 求离散对数

知b c求a*

x 满足

xab(modp)

难点+重点!!!
先讨论p为素数的情况

设g为p的一原根,记k关于g的离散对数(mod p)为 indgk ,则有

aindgxindgb(modϕ(p))

t1=indgb , 则有
gt1b(modp)

那么 t1 即与情形2一致,可由BSGS算法求出 t1
t2=indgx , 则原式变为
at2t1(modϕ(p))

其中只有 t2 是未知的, 可由扩展欧几里德求出所有 t2 的值。
又由 t2=indgx
gt2x(modp)

与情形1一致,可由快速幂求出所有x值。
至此解决p为素数时的问题。

结合一道题目理解
Broot HDU - 3930

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define met(a,b) memset(a, b, sizeof(a));
#define IN freopen("in.txt", "r", stdin);
#define OT freopen("ot.txt", "w", stdout);
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int maxn = 1e6 + 100;
const LL INF = 0x7fffffff;
const int dir[5][2] = {0,0,-1,0,1,0,0,-1,0,1};
const LL MOD = 1e9+7;
const double eps = 1e-6;

LL a, b, p;

//工具
bool is[maxn]; LL prm[maxn], id;
LL getprm(LL n) {
    if(n == 1) return 0;
    LL k = 0; met(is, 1);
    is[0] = is[1] = 0;
    for(LL i = 2; i < n; ++i) {
        if(is[i]) prm[k++] = i;
        for(LL j = 0; j < k && (i*prm[j] < n); ++j) {
            is[i*prm[j]] = 0;
            if(i % prm[j] == 0) break;
        }
    }
    return k;
}

LL Euler(LL x) {    //素数的欧拉函数
    return x-1;
}

LL gcd(LL a, LL b){
    return b ? gcd(b, a%b) : a;
}
LL extgcd(LL a, LL b, LL& x, LL& y) {
    if (b == 0) { x=1; y=0; return a; }
    LL d = extgcd(b, a % b, x, y);
    LL t = x; x = y; y = t - a / b * y;
    return d;
}

//快速乘 -- a*b % mod
LL pow_mul(LL a, LL b, LL p) {
    LL r = 0; a %= p;
    while(b) {
        if(b&1) r = (r+a) % p;
        a = (a+a) % p;
        b >>= 1;
    }
    return r;
}

LL pow_mod(LL a, LL b, LL p) {
    LL r = 1; a %= p;
    while(b) {
        if(b&1) r = pow_mul(r, a, p);
        a = pow_mul(a, a, p);
        b >>= 1;
    }
    return r;
}

//求原根
LL fac[maxn], num[maxn], tot;
LL Factor(LL n){
    LL ans = 1, temp = n; tot = 0;
    for (LL i = 0; i < id && prm[i] * prm[i] <= temp; i++){
        if (n % prm[i] == 0){
            fac[tot] = prm[i], num[tot] = 0;
            while (n%prm[i] == 0) n /= prm[i], ++num[tot];
            ans *= (num[tot] + 1);
            ++tot;
        }
    }
    if (n != 1){
        fac[tot] = n, num[tot] = 1;
        ans *=(num[tot]+1);
        ++tot;
    }
    return ans;
}

LL root(LL p) {
    LL phi = Euler(p);
    Factor(phi);
    for(LL g = 2; ; g++) {
        bool f = 1;
        for(int i = 0; i < tot; ++i) {
            LL t = phi / fac[i];
            if(pow_mod(g, t, p) == 1) { f = 0; break; }
        }
        if(f) return g;
    }
}

//BSGS
LL BSGS(LL a, LL b, LL p) {
    a %= p; b %= p;
    map h;
    LL m = ceil(sqrt(p)), x, y, d, t = 1, v = 1;
    for(LL i = 0; i < m; ++i) {
        if(h.count(t)) h[t] = min(h[t], i);
        else h[t] = i;
        t = pow_mul(t, a, p);
    }
    for(LL i = 0; i < m; ++i) {
        d = extgcd(v, p, x, y);
        x = (x* b/d % p + p) % (p);
        if(h.count(x)) return i*m + h[x];
        v = pow_mul(v, t, p);
    }
    return -1;
}

//求模线性方程
LL modeq(LL a, LL b, LL p, LL r[]) {
    LL e, i, d, x, y;
    d = extgcd(a, p, x, y);
    if (b % d) { return -1; }
    e = (x * (b / d) + p) % p;
    for (i = 0; i < d; i++) {
        r[i] = (e + i*(p/d) + p) % p;
    }// 总共 (a, m) 个解
    return d;
}

//开始解决问题
LL solve(LL a, LL b, LL p, LL r[], LL ans[]) {
    LL g = root(p);
    LL t1 = BSGS(g, b, p);
    LL phi = Euler(p);
    LL cnt = modeq(a, t1, phi, r);
    if(cnt == -1) return -1;
    for(int i = 0; i < cnt; ++i) {
        ans[i] = pow_mod(g, r[i], p);
    }
    return cnt;
}

LL ans[maxn], res[maxn];

int main() {
    #ifdef _LOCAL
    IN; //OT;
    #endif // _LOCAL
    id = getprm(maxn-1); LL kase = 0;
    while(scanf("%lld%lld%lld", &a, &p, &b) == 3) {
        LL cnt = solve(a, b, p, res, ans);
        printf("case%lld:\n", ++kase);
        if(cnt == -1) { printf("-1\n"); continue; }
        sort(ans, ans + cnt);
        for(int i = 0; i < cnt; ++i) printf("%lld\n", ans[i]);
    }

    return 0;
}

p为合数的情况
这种情况有点复杂,不过大体可分三部分解决问题,建立在前面的基础之上,就容易理解一些。

首先将 p 分解素因子为

p=pe11pe22pekk

那么我们对每一个 i 属于 [1,,k] ,都有
xab(modpeii)

我们可利用上面的知识去解出这里的x,不过由于模数不一定为素数的原因, 不能单纯地使用上面p为素数的方法去解,关于这个式子的解法是解决整个问题的关键,下面会给出细节,此处暂且认为已经解出x的一组解 {xi1,xi2,,xiti}
那么上面k个式子就会有k组这样的解, 我们从每组里取一个x值出来,记为 {X1,X2,,Xk} ,那么如果我们能找到一个Z满足
zX1(modpe11)zX2(modpe22)zXk(modpekk)

那么 Z 必为原式的一个解。
而要解这个,只需使用CRT即可, 同时我们需要一个dfs来枚举所有X可能的组合。
至此解决问题。

下面来看如何解决 xab(modpeii) 的解的问题。

首先,我们回想上面 p 为素数的情况,在整个操作中我们用到了 p 的原根 g ,和 b x 关于 g 的离散对数(mod p)。
换句话数,如果p具有原根g,且b和x有关于g的离散对数(mod p)。那么p是不是素数就都能用上面的方法去做了。
这也是我们要分三种情况去讨论的依据。

首先来看第一种情况, pi 没有原根的情况。
 我们知道, 一个数m有原根的充要条件是 m=1,2,4,pe,2pe , 其中p为奇素数, e为正整数。那么只有 pi=2 的时候, peii 才没有原根,所以当 pi=2 的时候, 我们不能像上面那样处理。
 一个有效的方法是,直接枚举 [0,,peii] 之间的 x , 将满足上式的x存入结果中即可。

第二种情况,b没有关于g的离散对数(mod p)。
 容易理解,只有0没有关于g的离散对数,那么如果 b0(modpeii) ,则b无离散对数,不能用上面的方法处理。
 这个时候, 我们肯定可以找一到一个最小的 t ,满足

(pti)ab0(modpeii)

 所以上式的解一定为 x=0,pti,2pti,3pti, , 且总共有 peiipti 个解。

最后一种情况。
除了上面两种以外,剩下的就可以按照p为素数时的方式处理了。

结合一道题目理解
X^A Mod B 51Nod - 1123

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define met(a,b) memset(a, b, sizeof(a));
#define IN freopen("in.txt", "r", stdin);
#define OT freopen("ot.txt", "w", stdout);
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int maxn = 1e6 + 100;
const LL INF = 0x7fffffff;
const int dir[5][2] = {0,0,-1,0,1,0,0,-1,0,1};
const LL MOD = 1e9+7;
const double eps = 1e-6;

LL a, b, p;

bool is[maxn]; LL prm[maxn], id;
LL getprm(LL n) {
    if(n == 1) return 0;
    LL k = 0; met(is, 1);
    is[0] = is[1] = 0;
    for(LL i = 2; i < n; ++i) {
        if(is[i]) prm[k++] = i;
        for(LL j = 0; j < k && (i*prm[j] < n); ++j) {
            is[i*prm[j]] = 0;
            if(i % prm[j] == 0) break;
        }
    }
    return k;
}

/*
LL Euler(LL x) {    //素数的欧拉函数
    return x-1;
}*/

LL Euler(LL x) {
    LL ans = x, m = (LL)sqrt(x*1.0)+1;
    for(LL i = 2; i < m; ++i) if(x%i == 0) {
        ans = ans / i * (i-1);
        while(x%i == 0) x /= i;
    }
    if(x > 1) ans = ans / x * (x-1);
    return ans;
}

LL gcd(LL a, LL b){
    return b ? gcd(b, a%b) : a;
}
LL extgcd(LL a, LL b, LL& x, LL& y) {
    if (b == 0) { x=1; y=0; return a; }
    LL d = extgcd(b, a % b, x, y);
    LL t = x; x = y; y = t - a / b * y;
    return d;
}

//快速乘 -- a*b % mod
LL pow_mul(LL a, LL b, LL p) {
    LL r = 0; a %= p;
    while(b) {
        if(b&1) r = (r+a) % p;
        a = (a+a) % p;
        b >>= 1;
    }
    return r;
}

LL pow_mod(LL a, LL b, LL p) {
    LL r = 1; a %= p;
    while(b) {
        if(b&1) r = pow_mul(r, a, p);
        a = pow_mul(a, a, p);
        b >>= 1;
    }
    return r;
}


LL Factor(LL n, LL fac[], LL num[], LL& tot){
    LL ans = 1, temp = n; tot = 0;
    for (LL i = 0; i < id && prm[i] * prm[i] <= temp; i++){
        if (n % prm[i] == 0){
            fac[tot] = prm[i], num[tot] = 0;
            while (n%prm[i] == 0) n /= prm[i], ++num[tot];
            ans *= (num[tot] + 1);
            ++tot;
        }
    }
    if (n != 1){
        fac[tot] = n, num[tot] = 1;
        ans *=(num[tot]+1);    //n的素因数中最多只有一个大于根号n的;
        ++tot;
    }
    return ans;
}

LL fac[maxn], num[maxn];
LL root(LL p, LL phi) {
    //LL phi = Euler(p);
    LL tot;
    Factor(phi, fac, num, tot);
    for(LL g = 2; ; g++) {
        bool f = 1;
        for(int i = 0; i < tot; ++i) {
            LL t = phi / fac[i];
            if(pow_mod(g, t, p) == 1) { f = 0; break; }
        }
        if(f && pow_mod(g, phi, p) == 1) return g;
    }
    return -1;
}

LL modeq(LL a, LL b, LL p, LL r[]) {
    LL e, i, d, x, y;
    d = extgcd(a, p, x, y);
    if (b % d) { return -1; }
    e = (x * (b / d) + p) % p;
    for (i = 0; i < d; i++) {
        r[i] = (e + i*(p/d) + p) % p;
    }// 总共 (a, m) 个解
    return d;
}

LL CRT(LL a[], LL m[], LL k) {
    LL i, d, x, y, Mi, ans = 0, M = 1;
    for (i = 0; i < k; i++) M *= m[i];  // !  注意不能overflow
    for (i = 0; i < k; i++) {
        Mi = M / m[i];
        d = extgcd(m[i], Mi, x, y);     // y 为逆元 -- Mi*y === 1 (% m[i])
        ans = (ans + a[i]*y*Mi) % M;
    }
    if (ans >= 0) return ans;
    else return (ans + M);
}

LL exBSGS(LL a, LL b, LL p) {
    a = (a%p+p)%p; b = (b%p+p)%p;
    LL ret = 1;
    for(LL i = 0; i <= 50; ++i) {
        if(ret == b) return i;
        ret = (ret*a) % p;
    }//枚举比较小的i

    LL x,y,d, v = 1, cnt = 0;
    while((d = gcd(a, p)) != 1) {
        if(b % d) return -1;
        b /= d, p /= d;
        v = (v * (a/d)) % p;
        ++cnt;
    }//约分直到(a, p) == 1

    map h;
    LL m = ceil(sqrt(p)), t = 1;
    for(LL i = 0; i < m; ++i) {
        if(h.count(t)) h[t] = min(h[t], i);
        else h[t] = i;
        t = (t*a) % p;
    }
    for(LL i = 0; i < m; ++i) {
        d = extgcd(v, p, x, y);
        x = (x* (b/d) % p + p) % p;
        if(h.count(x)) return i*m + h[x] + cnt;
        v = (v*t) % p;
    }
    return -1;
}


LL F[maxn], N[maxn], P[maxn], E[maxn], r[maxn], tot;
vector< vector > ans;
vector ot;

LL A[maxn], M[maxn];
void dfs(LL dep, LL N){
    if( dep == N ){ ot.push_back(CRT(A, M, N)); }
    else {
        for(LL i = 0; i < ans[dep].size(); ++i) {
            A[dep] = ans[dep][i];
            dfs(dep+1, N);
        }
    }
}

LL solve(LL a, LL b, LL p, LL r[]) {
    //分解因式
    Factor(p, F, N, tot);
    for(LL i = 0; i < tot; ++i) {
        P[i] = pow_mod(F[i], N[i], p*2);
        E[i] = P[i] - P[i]/F[i];
    }

    ans.clear();
    for(LL i = 0; i < tot; ++i) {
        vector res;
        if(F[i] == 2) {
            LL tb = (b%P[i]+P[i])%P[i];
            for(LL j = 0; j < P[i]; ++j) {
                if(pow_mod(j, a, P[i]) == tb) res.push_back(j);
            }
            if(res.size() == 0) return -1;
            sort(res.begin(), res.end());
            res.erase(unique(res.begin(), res.end()), res.end());
            ans.push_back(res);
            continue;
        }
        if(b % P[i] == 0) {
            LL x = 0, ret = 1;
            while(pow_mod(ret, a, P[i]) != 0) ret *= F[i];
            for(int j = 0; j < P[i]/ret; ++j) res.push_back(ret*j);
            sort(res.begin(), res.end());
            res.erase(unique(res.begin(), res.end()), res.end());
            ans.push_back(res);
            continue;
        }

        LL tp = P[i], tb = b%tp,te = E[i];//, d = gcd(tp, tb);
        LL g = root(tp, te);        if(g == -1) return -1;  //求原根
        LL t1 = exBSGS(g, tb, tp);     if(t1 == -1) return -1; //求离散对数
        LL cnt = modeq(a, t1, te, r); if(cnt == -1) return -1;//求log_gX
        for(LL j = 0; j < cnt; ++j) {
            res.push_back(pow_mod(g, r[j], tp));
        }
        sort(res.begin(), res.end());
        res.erase(unique(res.begin(), res.end()), res.end());
        ans.push_back(res);
    }

    //CRT合并
    ot.clear();
    for(LL i = 0; i < tot; ++i) M[i] = P[i];
    dfs(0, tot);
    sort(ot.begin(), ot.end());
    ot.erase(unique(ot.begin(), ot.end()), ot.end());

}

int main() {
    #ifdef _LOCAL
    IN; //OT;
    #endif // _LOCAL

    id = getprm(maxn-1);
    int t; cin >> t;
    while(t--) {
        scanf("%lld%lld%lld", &a, &p, &b);
        if(solve(a, b, p, r) == -1) printf("No Solution\n");
        else {
            for(LL i = 0; i < ot.size(); ++i) printf("%lld ", ot[i]);
            printf("\n");
        }
    }

    return 0;
}

还有一道练手题
God of Number Theory HDU - 3731

你可能感兴趣的:(数论,知识点)