bzoj 2480——扩展BSGS

题意

给定 $a,b$ 和模数 $p$,求整数 $x$ 满足 $a^x \equiv  b(mod \ p)$,不保证 $a,p$ 互质。

(好像是权限题,可见洛谷P4195

分析

之前讲过,可以通过设置 $x = km - r$ 而非 $x = km + r$ 避免求逆元,但是需要逆元存在,$a, p$ 互质的条件保证了这一点。

如果 $a, p$ 不互质怎么办呢?

我们想办法让他们变得互质。

具体地,设 $d_1 = gcd(a, p)$,如果 $d_1 \nmid b$,则原方程无解。否则我们把方程同时除以 $d_1$,得到

$$\frac{a}{d_1}\cdot a^{x-1} \equiv \frac{b}{d_1} \ mod (\frac{p}{d_1})$$

如果 $a$ 和 $\frac{p}{d_1}$ 仍不互质就再除,设 $d_2=gcd(a, \frac{p}{d_1})$。如果 $d2 \nmid \frac{b}{d_1}$,则方程无解;否则同时除以 $d_2$ 得到

$$\frac{a^2}{d_1d_2}\cdot a^{x-2} \equiv \frac{b}{d_1d_2} \ mod(\frac{p}{d_1d_2})$$

这样不停地判断下去,直到 $a \perp \frac{p}{d_1d_2...d_k}$。

记 $D = \prod_{i=1}^kd_i$,于是方程就变成了这样:

$$\frac{a^k}{D}\cdot a^{x-k} \equiv \frac{b}{D} \ mod(\frac{p}{D})$$

由于 $a \perp \frac{p}{D}$,于是推出 $\frac{a^k}{D} \perp \frac{p}{D}$。这样 $\frac{a^k}{D}$ 就有逆元了,于是把它丢到方程的右边,就是一个普通的BSGS问题了,于是求解 $x-k$ 再加上 $k$ 就是原方程的解。

$\frac{a^k}{D}$ 可能很大,事实上可以随手模 $\frac{p}{D}$(显然)。

注意,不排除解小于等于 $k$,所以在消因子之前做 $O(k)$ 枚举,直接验证 $a^i \equiv b\ mod(p)$,就能避免这种情况。

这个复杂度已经有点玄学了,普通的BSGS的复杂度为 $O(\sqrt p logp)$。洛谷上100组,$a, b, p \leq 1e9$,map不开O2优化会超时,需要开O2优化或者使用unordered_map。

代码

#include <cstdio>
#include <cmath>
#include <map>
using namespace std;
typedef long long ll;

ll gcd(ll a, ll b)
{
    return b ? gcd(b, a%b) : a;
}

ll qpow(ll a, ll b, ll p)
{
    a = a % p;
    ll ret = 1;
    while(b)
    {
        if(b&1)  ret = ret * a % p;
        a = a * a %p;
        b >>= 1;
    }
    return ret % p;
}

ll extend_bsgs(ll a, ll b, ll p)  //a^x=b(mod p),a,p不一定互质,不存在返回-1
{
    ll _a = a, _b = b, _p = p;
    a %= p; b %= p;
    if (a == 0)
        return b > 1 ? -1 : b == 0 && p > 1;
    ll g, cnt = 0, q = 1;
    while ((g = gcd(a, p)) != 1) {
        if (b == q)  return cnt;
        if (b % g)  return -1;
        ++cnt;
        b /= g;
        p /= g;
        q = a/g*q%p;  //可以随手取模
    }

    ll tmp = 1;
    for(int i = 0;i <= cnt;i++)   //枚举小于等于cnt的(好像也不是必须的
    {
        if(tmp % _p == _b)  return i;
        tmp = tmp * _a % _p;
    }

    map<ll, ll> x;
    ll m = sqrt(p);
    for (ll i = 1, t = b*a%p; i <= m; ++i, t = t*a%p)
        x[t] = i;
    for (ll i = m, t = qpow(a, m, p); i-m < p-1; i += m)
        if (q = q*t%p, x.count(q))
            return i-x[q]+cnt;
    return -1;
}

int main()
{
    ll a, p, b;
    while (scanf("%lld %lld %lld", &a, &p, &b), p) {
        ll ans = extend_bsgs(a, b, p);
        if (ans == -1)
            puts("No Solution");
        else
            printf("%lld\n", ans);
    }
    return 0;
}

 

 

参考链接:

1. 大步小步算法(BSGS)及扩展  & bzoj2480

2. OI WIKI——BSGS算法

posted @ 2019-09-10 10:35  Rogn  阅读(238)  评论(0编辑  收藏  举报