POJ 3243 clever Y

扩展BSGS

朴素BSGS只能处理模数 \(p\) 是质数的情况,但是对于其他情况,我们就可以用扩展BSGS解决
计算 \(a^x \equiv b \pmod p\) ,如果 \(gcd(a, p) == 1\),那么就是朴素BSGS,如果 \(gcd(a, p) \not = 1\) ,根据带余除法的性质,我们可以在同余方程的 \(a, p, b\)同时除以一个数,方程的解不变.
\(d = gcd(a, p)\) ,首先将 \(a\)\(b\) 都对 \(p\) 取模,然后特判, 如果 \(b == 1\),那么 \(x\) 一定等于 \(0\) ,如果 \(a\)\(p\) 的倍数,那么如果 \(b\) 也是 \(p\) 的倍数,那么答案是 \(1\) ,如果 \(b\)\(1\) ,那么答案是 \(0\) ,否则无解

特判完了以后,判断 \(d \mid b\) 是否成立, 如果不成立, 显然无解,如果成立,那么将这三个数同时除以 \(d\) ,原式变为 \(a/b \times a^{x-1} \equiv b/p \mod (p/d)\), 当然可能 \(gcd(a, p/b) !=1\),所以我们要不停的这么做,直到 \(a\)\(p/d\) 互质, 设我们这样做了 \(k\) 次,最后得到
\[\frac {a^k} {\prod_{i=1} ^ k d } \times a^{x -k} \equiv \frac b {\prod_{i=1} ^ k d } \pmod \frac p {\prod_{i=1} ^ k d }\]
我们有可能在操作的过程中发现, \(x<k\),所以我们在每一步操作的时候都要判断一下等式是否成立,如果 \(\frac {a^k} {\prod_{i=1} ^ k d} == \frac b {\prod_{i=1} ^ k d }\),n那么答案就是 \(k\) ,如果在操作的过程中发现 \(d \nmid b\),那么\(x\) 只能是 \(0\) .

如果以上情况都没有出现,那么就转化成了一个朴素BSGS问题,答案就是算下的结果加上 \(k\)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#define ll long long
using namespace std;
ll a, b, p;
struct HashTable{
    static const int MOD = 99901, MAXN = 200005;
    ll dat[MAXN], nxt[MAXN], head[MAXN], id[MAXN], nume;
    void clear() {
        memset(head, 0, sizeof(head));
        memset(nxt, 0, sizeof(nxt));
        nume = 0;
    }
    void insert(ll x, ll y){
        ll tmp = x % MOD;
        for(int i = head[tmp]; i; i = nxt[i]) {
            if(dat[i] == x) {id[i] = y; return;}
        }
        dat[++nume] = x; id[nume] = y;
        nxt[nume] = head[tmp]; head[tmp] = nume;
    }
    ll query(ll x) {
        ll tmp = x % MOD;
        for(int i = head[tmp]; i; i = nxt[i]) {
            if(dat[i] == x) return id[i];
        }
        return -1;
    }
}Hash;
ll gcd(ll a, ll b){
    return !b ? a : gcd(b, a % b);
}
ll quick_mod(ll a, ll k, ll p) {
    ll ans = 1;
    while(k) {
        if(k & 1ll) (ans *= a) %= p;
        (a *= a) %= p;
        k >>= 1;
    }
    return ans;
}
ll exBSGS(ll a, ll b, ll p) {
    Hash.clear();
    a %= p; b %= p;
    if(b == 1) return 0;
    if(!a){
        if(!b) return 0;
        return -1;
    }
    ll t, d = 1, k = 0;
    do{
        t = gcd(a, p);
        if(b % t) return -1;
        b /= t; p /= t;
        (d *= a/t) %= p;
        k++;
        if(d == b) return k;
    }while(t != 1);
    ll m = ceil(sqrt(p)), tmp = b;
    for(int i = 0; i <= m; i++) {
        Hash.insert(tmp, i);
        (tmp *= a) %= p;
    }
    ll g = quick_mod(a, m, p);
    tmp = (d * g) % p;
    for(int i =1; i <= m; i++) {
        ll q = Hash.query(tmp);
        if(q != -1) return i * m - q + k;
        (tmp *= g) %= p;
    }
    return -1;
}
int main() {
    while(cin >> a >> p >> b) {
        if(!a && !p && !b) break;
        ll t = exBSGS(a, b, p);
        if(t == -1) printf("No Solution\n");
        else printf("%lld\n", t);
    }
    return 0;
}

转载于:https://www.cnblogs.com/Mr-WolframsMgcBox/p/8551108.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值