BSGS
BSGS,全称叫 BabyStepGiantStep,也就是大步小步
其实还是比较暴力的
它可以(O(sqrt p))的复杂度内解出:
中(x)的值
如果(gcd(a,p)
eq 1)就要用到 exBSGS 了
我们考虑令(x=im-k,0le k<m)
那么原式变为
那么,我肯可以枚举(kin[0,m)),计算出(na^kmod p)的值,然后把对应的(k)存下来
可以用map
或 hash,用map
会多一个(log)
然后,再枚举(iin[0,lceilfrac{p}{m}
ceil]),计算出(a^{im}mod p)
然后看一看之前有没有一个(k),使得(na^kequiv a^{im}pmod p),且(im>=k)
如果有,那么答案肯定就是(im-k)
如果一直没找到,那么就无解
复杂度是(O(max(m,frac{p}{m} ))),显然,让(m=lceil sqrt p ceil)时最优,即为(O(sqrt p))
但是,我们考虑的只是(xin[1,p])的情况,那为什么要找的这个(x)一定满足这样的性质呢?
首先(gcd(a,p)=1),满足欧拉定理的条件,也就是会有(a^{varphi(p)}equiv 1pmod pRightarrow a^{qvarphi(p)}equiv 1pmod p,qinmathbb{N^*})
那么,(kmod varphi(p)=k-qvarphi(p)<varphi(p)<p)
则(a^{kmod varphi(p)}=dfrac{a^k}{a^{qvarphi(p)}})
又由于那个分母在(mod p)中同余于(1)
所以(a^{kmod varphi(p)}equiv a^kpmod p)
则可以得知,要求的最小的(x),如果有解,一定满足(x<varphi(p)<p)
模板题:P3846 [TJOI2007] 可爱的质数
这题保证了(a,n<p),所以不需要一些奇奇怪怪的特判
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<iomanip>
#include<cstring>
#include<map>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
register int x=0;register int y=1;
register char c=std::getchar();
while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
return y?x:-x;
}
std::map<int,int>map;
inline int power(int a,int b,int p){
int ret=1;
while(b){
if(b&1) ret=1ll*ret*a%p;
b>>=1;a=1ll*a*a%p;
}
return ret;
}
inline int BSGS(int a,int n,int p){
reg int m=std::ceil(std::sqrt(p));
for(reg int i=0,s=n;i<m;i++,s=1ll*s*a%p) map[s]=i;
for(reg int i=0,tmp=power(a,m,p),s=1;i<=m;i++,s=1ll*s*tmp%p)
if(map.find(s)!=map.end())
if(i*m>=map[s]) return i*m-map[s];
return -1;
}
int main(){
int p=read(),a=read(),n=read();
LL ans=BSGS(a,n,p);
ans==-1?std::puts("no solution"):std::printf("%lld",ans);
return 0;
}
exBSGS
用于处理上面哪个问题(gcd(a,p)
eq 1)的情况
有些细节稍显恶心(其实还好
首先,原来那个同余方程可以等价的写成:
设(gcd(a,p)=d)
由于斐蜀定理,这个式子有解的充要条件是(dmid n),否则直接返回无解
那么上面的式子就是:
此时,(a^{x-1}
ightarrow a^x,frac{p}{d}
ightarrow p,frac{n}{d}
ightarrow n)
那么一直递归,直到(gcd(a,p)=1)
所以此时设一共递归了(cnt)次,这所有次递归的(d)的乘积是(d)(用一个字母说起来省事
原式就是
那么我们就用(a'=a,n'=frac{n}{d},p'=frac{p}{d})来跑一次 BSGS 就行了
当然,答案还要再加上(cnt)
而且,还有一个(dfrac{a^{cnt}}{d})的系数要乘上,具体对应代码中的变量ad
,要传参到 BSGS 的函数里
模板题:SP3105 MOD - Power Modulo Inverted,P4195 【模板】exBSGS
还是双倍经验,但是这个不保证(a,n<p),所以要先模一下,然后判断一下是不是等于(0),具体看代码
而且这个还卡常,必须要用unordered_map
,它的内部实现是哈希表,而map
是红黑树,前者构建好像稍慢,但是查询快,适合这个题
话说我写这题的时候听说卡常,然后T了以后猛卡常数,结果后来发现是写法错了,于是就对着错误的程序卡了一晚上常直至自闭
不过是 c++11 的东西,noip可能不能用,编译时要加上-std=c++11
当然写 hash 也可以,但我不太会/kk
#include<cstdio>
#include<map>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<iomanip>
#include<cstring>
#include<unordered_map>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
register int x=0;register int y=1;
register char c=std::getchar();
while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
return y?x:-x;
}
std::unordered_map<int,int>map;
int gcd(int a,int b){return b?gcd(b,a%b):a;}
inline int BSGS(int a,int n,int p,int ad){
map.clear();
reg int m=std::ceil(std::sqrt(p));
reg int s=1;
for(reg int i=0;i<m;i++,s=1ll*s*a%p) map[1ll*s*n%p]=i;
for(reg int i=0,tmp=s,s=ad;i<=m;i++,s=1ll*s*tmp%p)
if(map.find(s)!=map.end())
if(1ll*i*m-map[s]>=0) return 1ll*i*m-map[s];
return -1;
}
inline int exBSGS(int a,int n,int p){
a%=p;n%=p;
if(n==1||p==1) return 0;
reg int cnt=0;
reg int d,ad=1;
while((d=gcd(a,p))^1){
if(n%d) return -1;
cnt++;n/=d;p/=d;
ad=(1ll*ad*a/d)%p;
if(ad==n) return cnt;
}
// std::printf("a: %d n: %d p: %d ad:%d
",a,n,p,ad);
LL ans=BSGS(a,n,p,ad);
if(ans==-1) return -1;
return ans+cnt;
}
signed main(){
int a=read(),p=read(),n=read();
while(a||p||n){
int ans=exBSGS(a,n,p);
if(~ans) std::printf("%d
",ans);
else std::puts("No Solution");
a=read();p=read();n=read();
}
return 0;
}