[日常摸鱼]poj2417 DiscreteLoggingBSGS算法

在这题TLE了一天…T_T

BSGS裸题…不知道为什么一直挂

第二天(也就是今天)换成黄学长博客里的写法就过掉了


题意:解关于$x$的方程:$a^x \equiv b \pmod{p}$,$p$为质数,有多解则输出最小的那个

(和原题里的字母不一样x)

这玩意好像叫离散对数。

首先得注意到$x$的取值只有可能是$0~p-1$,因为费马小定理告诉我们$a^{p-1} \equiv a^{0} \pmod{p}$,这时候出现了循环

不过直接枚举复杂度还是不行的

考虑把$x$写成$x=k*m+i$的形式(这里先不说$m$取多少),原方程变成$a^{km+i} \equiv b \pmod{p}$

移过去:$a^i \equiv b*a^{-km} \pmod{p}$

然后可以先处理出所有可能的$i$(即$i=0,1,...,m-1$)所对应的$a^i$,然后把每个结果和对应的最小的$i$存到map里面

再枚举$k$,根据需要的结果查找所对应的的最小$i$,这里计算$a^{-km}$可以递推

根据费马小定理$1 \equiv a^{p-1} \pmod{p}$两边乘上$a^{-m}$就可以得到$a^{-m} \equiv a^{p-1-m} \pmod{p}$

然后我们可以根据$k$来递推$a^{-km}$

然后来看看怎么取$m$,预处理所有的$a^i$一共是$m$个,丢到map里复杂度也是就是$O(mlogm)$

对于给定的$m$,要枚举$\frac{p}{m}$个$k$,每次也是log的复杂度,总复杂度$O((m+\frac{p}{m})log m)$

所以当$m$取$\sqrt{p}$时复杂度最优,复杂度$O(\sqrt{p}log \sqrt{p})$

实现的地方把map[1]=0写成map[1]=m+1(一个取不到的值)应该也是为了方便后面的判断

#include<cstdio>
#include<cmath>
#include<map>
#include<cstring>
#include<algorithm>
using namespace std;

typedef long long lint;

map<lint,lint>x;

inline lint mul_mod(lint a,lint b,lint p)
{
    lint res=a%p*b%p;res=(res%p+p)%p;return res;
}

inline lint pow_mod(lint a,lint b,lint p)
{
    lint res=1;
    for(;b;b>>=1,a=(a*a)%p)if(b&1)res=(res*a)%p;
    return res;
}

inline lint BSGS(lint a,lint b,lint p)
{
    a%=p;x.clear();
    if(!a&&!b)return 1;
    if(!a)return -1;
    lint m=ceil(sqrt(p)),t=1;
    x[1]=m+1;
    for(register int i=1;i<m;i++)
    {
        t=mul_mod(t,a,p);
        if(!x[t])x[t]=i;
    }
    lint tmp=pow_mod(a,p-m-1,p),inv=1;
    for(register int k=0;k<m;k++)
    {
        lint i=x[b*inv%p];
        if(i)
        {
            if(i==m+1)i=0;
            return k*m+i;
        }
        inv=mul_mod(inv,tmp,p);
    }
    return -1;
}

int main()
{
    //freopen("input.in","r",stdin);
    lint res,b,n,p;
    while(scanf("%lld%lld%lld",&p,&b,&n)==3)
    {
        res=BSGS(b,n,p);
        if(res==-1)puts("no solution");
        else printf("%lld\n",(res%p+p)%p);
    }
    return 0;
}
View Code

如果有说错还请在评论区怼我

原文地址:https://www.cnblogs.com/yoshinow2001/p/7979008.html