浅学大步小步/BSGS

前言

  • 哦哦哦哦哦哦哦哦哦开始学大步小步了哦哦哦哦哦哦哦哦哦!
  • pb 强强强强强哦哦哦哦哦哦哦哦哦!

介绍

  • \(\mathcal{BSGS}\)\(\mathcal{Baby~Step~Giant~Step}\)),直译叫婴儿步和巨人步,通常称为大步小步算法,常用于在 \(O(\sqrt n)\) 的复杂度内求解离散对数问题
  • 就是求这个柿子:

\[a^x \equiv b~~~( \!\!\!\!\!\!\mod p) \]

  • 其中 \(a\)\(p\) 互质且 \(0 \le x < p\)

算法流程

  • 我们令 \(x = A \times \left\lceil\sqrt p\right\rceil - B\),其中,\(0\le A,B \le \left\lceil\sqrt p\right\rceil\)
  • 直接带入 \(x\),有:

\[a^{A \times \left\lceil\sqrt p\right\rceil - B} \equiv b~~~( \!\!\!\!\!\!\mod p) \]

  • \(a\) 改一下,有:

\[\frac{a^{A \times \left\lceil\sqrt p\right\rceil}}{a^B} \equiv b~~~( \!\!\!\!\!\!\mod p) \]

  • 由于 \(a⊥p\),所以可以把 \(a^B\) 直接乘过去,有:

\[a^{A \times \left\lceil\sqrt p\right\rceil} \equiv ba^B~~~( \!\!\!\!\!\!\mod p) \]

  • 由于 \(A\)\(B\) 都不大于 \(\left\lceil\sqrt p\right\rceil\),所以我们可以直接枚举,先枚举 \(B\),利用哈希表映射(\(ba^B \!\!\!\mod p \to B\)),再枚举 \(A\),查看 \(a^{A \times \left\lceil\sqrt p\right\rceil} \!\!\!\mod p\) 是否出现过,出现过直接计算 \(x\)\(x\) 的表达式上述已有提及
  • 如果你选择手写哈希表,复杂度是 \(O(\sqrt p)\),选择 \(map\) 会带一只老哥

例题P3846 【模板】BSGS / [TJOI2007] 可爱的质数

  • 按上述做就行
  • 其实直接在开根函数上套上取整就行,开根函数返回 double
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
constexpr int maxn=1e5+10;
int read()
{
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0' || ch>'9')
    {
        if (ch == '-') f=-1;
        ch=getchar();
    }
    while (ch>='0' && ch<='9')
    {
        x=(x<<1)+(x<<3)+ch-'0';
        ch=getchar();
    }
    return x*f;
}
int p,a,b;
map<int,int> mp;
int power(int x,int y)
{
    int res=1;
    while (y)
    {
        if (y&1) (res*=x)%=p;
        (x*=x)%=p;
        y>>=1;
    }
    return res;
}
signed main()
{
    p=read(),a=read(),b=read();
    int k=ceil(sqrt(p*1.0)*1.0);
    for (int i=1;i<=k;i++)
        mp[b*power(a,i)%p]=i;
    int x=-114514;
    for (int i=1;i<=k;i++)
        if (mp[power(a,i*k)])
        {
            x=i*k-mp[power(a,i*k)];
            break;
        }
    if (x == -114514) puts("no solution");
    else printf("%lld\n",x);
	return 0;
}

例题P2485 [SDOI2011] 计算器

  • 啊啊啊啊啊啊啊啊啊我代码交错地方了我存的代码啊啊啊啊啊啊啊啊
  • 操作 \(1\) 是快速幂板子,操作 \(2\) 是逆元板子,操作 \(3\)\(\mathcal{BSGS}\) 板子
  • 板子大战
  • \(51 \to 67 \to 85 \to 100\)
  • 有非常多注意的
  1. 操作 \(2\) 如果答案为 \(0\) 即无解
  2. 操作 \(3\) 会因为 \(p \mid a\)\(p \mid b\) 而导致答案为 \(0\),实际为 \(1\),具体 \(\mathcal{BSGS}\) 的实现看看代码,就多了 \(3\) 个特判
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
constexpr int maxn=1e5+10;
int read()
{
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0' || ch>'9')
    {
        if (ch == '-') f=-1;
        ch=getchar();
    }
    while (ch>='0' && ch<='9')
    {
        x=(x<<1)+(x<<3)+ch-'0';
        ch=getchar();
    }
    return x*f;
}
int T,k;
int y,z,p;
int power(int x,int y)
{
    int res=1;
    while (y)
    {
        if (y&1) (res*=x)%=p;
        (x*=x)%=p;
        y>>=1;
    }
    return res;
}
int BSGS(int a,int b)
{
    if (a%p == 1 && b%p == 1) return 0;
    if (a%p == b%p) return 1;
    if (a%p == 0) return b%p == 1 ? 0 : -114514; 
    map<int,int> mp;
    int k=ceil(sqrt(p));
    for (int i=1;i<=k;i++)
        mp[b*power(a,i)%p]=i;
    int x=-114514;
    for (int i=1;i<=k;i++)
        if (mp[power(a,k*i)])
        {
            x=k*i-mp[power(a,k*i)];
            break;
        }
    // cerr << "x:" << x << endl;
    return x;
}
signed main()
{
    T=read(),k=read();
    while (T--)
    {
        y=read(),z=read(),p=read();
        if (k == 1)
            printf("%lld\n",power(y,z));
        if (k == 2)
        {
            int x=power(y,p-2)*z%p;
            if (!x) puts("Orz, I cannot find x!");
            else printf("%lld\n",x);
        }
        if (k == 3)
        {
            int x=BSGS(y,z);
            // cerr << x << endl;
            if (x == -114514) puts("Orz, I cannot find x!");
            else printf("%lld\n",x);
        }
    }    
	return 0;
}

后话

  • 更深的以后再补,喵喵喵
posted @ 2026-08-15 19:11  msjing  阅读(9)  评论(0)    收藏  举报