[日常摸鱼]poj2417 DiscreteLogging-BSGS算法
在这题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; }
如果有说错还请在评论区怼我