淺談BSGS和EXBSGS

2022-05-28 15:01:04

我的 BSGS 和各位犇犇的差不多,但是不需要求逆元

Luogu [ TJOI2007 ] 可愛的質數

原題展現

題目描述

給定一個質數 \(p\),以及一個整數 \(b\),一個整數 \(n\),現在要求你計算一個最小的非負整數 \(l\),滿足 \(b^l \equiv n \pmod p\)

輸入格式

僅一行,有 \(3\) 個整數,依次代表 \(p, b, n\)

輸出格式

僅一行,如果有 \(l\) 滿足該要求,輸出最小的 \(l\),否則輸出 no solution

樣例 #1

樣例輸入 #1
5 2 3
樣例輸出 #1
3

資料規模與約定

  • 對於所有的測試點,保證 \(2\le b,n < p<2^{31}\)

Baby Steps Giant Steps 詳解

注意到互質,根據尤拉定理,我們易得\(l< p\),列舉的時間複雜度為\(O(p)\)

其實可以優化到\(O(\sqrt{p})\),設 \(m=\lceil \sqrt{p}\rceil,r=b\%m\)

於是我們可以將 原式寫成

\[b^{km+r}\equiv n(mod\;p)\\ b^{km}\equiv nb^{-r}(mod\;p) \]

右邊好像要求逆元啊,我們不想求逆元,怎麼辦呢?

只需將式子改成

\[b^{km-r}\equiv n(mod\;p)\\ b^{km}\equiv nb^{r}(mod\;p) \]

解決了問題

我們考慮找到一個 \(k\) 和 一個 \(r\) 使得上述式子成立,這個並不難

首先列舉 \(r\) ,顯然有 \(r(1\leq r\leq m)\) 注意這裡和廣大打法不同

因為廣大打法是列舉餘數,這裡列舉的是相反的

然後把右邊式子的值雜湊存下,列舉左邊的 \(k(1\leq k \leq m)\)

對於左邊列舉求出的值看看雜湊陣列是否存在對應的右邊的值,如果有,那麼就是一個解

搞出一個最小的解好像也不是很難吧.....

時間複雜度 \(O(m)\) ,也就是 \(O(\sqrt{p})\)

然後注意一下,要打很多特判

上一下碼風巨醜的程式碼

inline ll ksc(ll x, ll y, const ll& p) { return (x * y - (ll)((long double)x / p * y) * p + p) % p; }
vector<pair<ll, int> > v[ 100013];
inline ll BSGS(ll a, ll b, const ll&p) {
        if (b == 1) {
        if (a == 0)
            return -1;
        return 1;
    }
    if (b == 0) {
        if (a == 0)
            return 1;
        return -1;
    }
    if (a == 0) {
        return -1;
    }
    ll m = ceil(sqrt(p)), cnt = 1, res = 1;
    for (int r = 1; r <= m; r++) {
        cnt = ksc(cnt, a, p);//這個龜速乘不是龜速乘
        v[(ksc(cnt, b, p)) % mod].push_back(make_pair(ksc(cnt, b, p), r));
    }
    for (int k = 1; k <= m; k++) {
        res = ksc(cnt, res, p);
        ll id=res%mod;
        if (v[id].size())
        {
            for (int j = v[id].size() - 1; j >= 0; j--)
            {
                if (v[id][j].first ==res)
                {
                    return m * k - v[id][j].second; 
                }                
            }                           
        }
    }
    return -1;
}

SPOJ3105 MOD

原題展現

題目描述

給定 \(a,p,b\),求滿足 \(a^x≡b \pmod p\) 的最小自然數 \(x\)

輸入格式

每個測試檔案中包含若干組測試資料,保證 \(\sum \sqrt p\le 5\times 10^6\)

每組資料中,每行包含 \(3\) 個正整數 \(a,p,b\)

\(a=p=b=0\) 時,表示測試資料讀入完全。

輸出格式

對於每組資料,輸出一行。

如果無解,輸出 No Solution,否則輸出最小自然數解。

樣例 #1

樣例輸入 #1
5 58 33
2 4 3
0 0 0
樣例輸出 #1
9
No Solution

資料範圍

對於 \(100\%\) 的資料,\(1\le a,p,b≤10^9\)\(a=p=b=0\)

擴充套件 Baby Steps Giant Steps 詳解

注意到不互質,那我們就要想辦法讓它互質

\[a^x\equiv b(mod\;p)\\ a^x-kp=b\\ 設 d=gcd(a,p)\\ 若 d|b 不成立,則無解\\ 式子除 d 得 a^{x-1}\frac a d- k\frac p d=\frac b d\\ 改記為a^{x-1}a'- kp'=b'\\ 即 a^{x-1}a'\equiv b'(mod\; p') \]

如此反覆,直到互質為止,差不多就是

\[a^{x-cnt}a'\equiv b'(mod\; p') \]

注意,操作時如果兩邊值相等了,答案就是 \(cnt\)

然後就是個普通 BSGS ,變了一點點,左邊需要乘上 \(a'\),其他都是一模一樣的

求出答案之後答案要加上 \(cnt\) ,因為我們求出的是 \(x-cnt\)

本題時限高達 4s ,就算不寫雜湊用 map 也能通過

參考如下實現

const ll mod=100003;
vector<pair<ll, int> > v[ 100013];
inline ll BSGS(ll a, ll b, const ll&p) {
    memset(v,0,sizeof(v));
        if (b == 1) {
        if (a == 0)
            return -1;
        return 1;
    }
    if (b == 0) {
        if (a == 0)
            return 1;
        return -1;
    }
    if (a == 0) {
        return -1;
    }
    ll m = ceil(sqrt(p)), cnt = 1, res = 1;
    for (int r = 1; r <= m; r++) {
        cnt = ksc(cnt, a, p);
        v[(ksc(cnt, b, p)) % mod].push_back(make_pair(ksc(cnt, b, p), r));
    }
    for (int k = 1; k <= m; k++) {
        res = ksc(cnt, res, p);
        ll id=res%mod;
        if (v[id].size())
        {
            for (int j = v[id].size() - 1; j >= 0; j--)
            {
                if (v[id][j].first ==res)
                {
                    return m * k - v[id][j].second; 
                }                
            }                           
        }
    }
    return -1;
}