HDU5377 Root(扩展欧几里得 + 生成元)

本文介绍了一种利用Baby-step-Giant-step算法解决高次同余方程A^x=B(modp)的方法,特别关注于求解最小的x。通过预处理生成元和使用扩展欧几里得算法,文章提供了一个高效的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/**
题意:给出一个整数sum, 查询q,每个查询有一对(x, y), 问满足x ^ k = y(mod p) 的最小的K是多少,不存在的话输出-1,其中p是sum的质因子

思路:有个baby-step-giant-step算法, 求高次同余方程A ^ x = B (mod p)最小的x, 因为p是素数, 那么有生成元g,使得g^a=A,g^b = B,生成元可以预处理, 那么得出a * x = b (mod p-1), 这是扩展欧几里得,因为sum <= 1e8, 那么>1e4的素数最多只有一个, 最这个特殊处理, 小的暴力解决, 对这个生成元g预处理之后,可以快速查找到a, b的值,然后就可以了。
*/

#include<bits/stdc++.h>
typedef long long ll;
const ll maxn = 1e6 + 10;
const ll INF = 1e9 + 10;
using namespace std;

ll prime[maxn], res[30][10010], num;
const ll hash_size = 1000101;

ll exgcd(ll a, ll mod, ll y) {  ///a * x = y % mod, 1求x
    ll X = a, Y = mod, md, q, xi, ansx = 1, ansy = 0;
    while(1) {
        q = X / Y; md = X - Y * q;
        if(!md) break;
        xi = ansx - q * ansy; ansx = ansy; ansy = xi;
        X = Y; Y = md;
    }

    ansy %= mod; if(ansy < 0) ansy += mod;
    if(y % Y) return -1;  ///Y是最大公约数
    ansy = (ansy * y / Y) % (mod / Y);
    return ansy;  ///ansy是逆元
}

struct HASH_TABLE {
    ll top, gm, inv, m, pri;
    ll hash_table[hash_size], mp[hash_size];
    ll head[hash_size], nxt[hash_size];
    void init() {
        top = 1;
        memset(hash_table, 0, sizeof hash_table);
        memset(nxt, 0, sizeof nxt);
        memset(head, -1, sizeof head);
    }
    void init_table(ll g, ll p) { ///生成元,素数p
        m = maxn; ll e = 1;
        for(ll i = 0; i < m; i++) {
            ll id = e % hash_size;
            if(~head[id]) nxt[top] = head[id];
            head[id] = top;
            hash_table[top] = e; mp[top] = i;
            e = e * g % p; top++;
        }
        gm = e; pri = p;
        inv = exgcd(gm, p, 1);
    }
    ll query(ll x) {
        for(ll i = 0; i <= pri / m; i++) {
            ll id = x % hash_size;
            ll hd = head[id];
            if(~hd) for( ; hd; hd = nxt[hd]) {
                if(hash_table[hd] == x) return i * m + mp[hd];
            }
            x = x * inv % pri;
        }
        return -1;
    }

} hs;

ll qmod(ll x, ll n, ll mod) {
    ll ans = 1;
    while(n) {
        if(n & 1) ans = ans * x % mod;
        x = x * x % mod;
        n >>= 1;
    }
    return ans;
}

void divide(ll x, vector<ll> &vec) { ///分解质因数
    for(ll i = 0; prime[i] * prime[i] <= x; i++) {
        ll t = prime[i];
        if(x % t) continue;
        vec.push_back(t);
        while(x % t == 0) x /= t;
    }
    if(x != 1) vec.push_back(x);
}

void init() {
    num = 0;
    for(ll i = 2; i < maxn; i++) {
        if(!prime[i]) prime[num++] = i;
        else continue;
        for(ll j = (ll)i * i; j < maxn; j += i) prime[j] = 1;
    }
}
ll T, n, m, kase = 1, sum, q;
vector<ll> G;

ll find_g(ll p) {
    if(p == 2) return 1;
    vector<ll> vs; divide(p - 1, vs);
    for(ll c = 2; ; c++) {
        ll flag = 1;
        for(ll j = 0; j < vs.size(); j++)
            if(qmod(c, (p - 1) / vs[j], p) == 1) { flag = 0; break; }
        if(flag) return c;
    }
}

///小范围暴力,大的数最多只有一个
void init_hash() {
    for(ll i = 0; i < G.size(); i++) {
        ll c = G[i], g = find_g(c), ds = 1;
        if(G[i] > 1e4) { hs.init(); hs.init_table(g, c); continue; }
        for(int j = 0; j < c - 1; j++) {
            res[i][ds] = j;
            ds = ds * g % c;
        }
    }
}

ll solve(ll id, ll x, ll y, ll p) {
    ll kx, ky;
    if(p < 1e4) kx = res[id][x], ky = res[id][y];
    else kx = hs.query(x), ky = hs.query(y);
    if(kx == -1 || ky == -1) return INF;
    ll ans = exgcd(kx, p - 1, ky);
    if(ans == -1) return INF;
    return ans;
}

int main() {
    init();
    scanf("%lld", &T);
    while(T--) {
        scanf("%lld %lld", &sum, &q);
        G.clear(); divide(sum, G);
        printf("Case #%lld:\n", kase++);
        init_hash();
        while(q--) {
            ll x, y, ans = INF;
            scanf("%lld %lld", &x, &y);
            for(ll i = 0; i < G.size(); i++) {
                ll p = G[i], xs = x % p, ys = y % p;
                ans = min(ans, solve(i, xs, ys, p));
            }
            if(ans == INF) printf("-1\n");
            else printf("%lld\n", ans);
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值