给定整数a,b,p,其中a,p互质(或者说p是质数),求一个非负整数x,使得\(a^x≡b(\mod p)\)
设\(x=i*t-j\),其中\(t=\sqrt q\)(向上取整),\(0<=j<=t-1\),则方程变为\(a^{i*t-j}≡b(\mod p)\),整理一下式子得\((a^t)^i≡b*a^j(\mod p)\)
对于所有的\(j(0<=j<=t-1)\),我们可以计算出\(b*a^j\mod p\)的值,并插入哈希表或者map中.
然后枚举i的所有取值\((0<=i<=t)\),同样可以计算出\((a^t)^i\mod p\)的值,然后在hash表中查找是否存在对应的j,如果有则\(x=i*t-j\)记录答案即可.
时间复杂度为\(O(\sqrt p)\)
代码实现:(我粗略地分为两种:第一种直接按照上述分析来的,很好理解,时间复杂度实际上是\(O(\sqrt p*log),log\)是因为快速幂;第二种就可以不用快速幂直接累乘上去,时间复杂度才是真正的\(O(\sqrt p)\))
LL BSGS(LL a,LL b,LL p){
if(a%p==0)return -1;//特判
map<LL,LL>hash;hash.clear();
//直接开个map存值,多组数据时记得清空
b%=p;
LL t=(int)sqrt(p)+1;
//计算根号p向上取整的值,用ceil也可以
for(int j=0;j<t;j++){
LL val=1LL*b*ksm(a,j,p)%p;
//这里经常要用到龟速乘
hash[val]=j;
}
//计算出b*a^j mod p的值,并存入map中
a=ksm(a,t,p);
//先算出a^t的值
if(a==0)return b==0?-1:1;//特判一下
for(int i=0;i<=t;i++){
LL val=ksm(a,i,p);
//即(a^t)^i
int j=hash.find(val)==hash.end()?-1:hash[val];
//到map中去找是否有对应的值
if(j>=0&&i*t-j>=0)return i*t-j;
//如果有就直接输出答案x=i*t-j
}
return -1;
//一直没找到则无解
}
LL BSGS(LL a,LL b,LL p){
if(a%p==0)return -1;
map<LL,LL>hash;hash.clear();
LL t=ceil(sqrt(p)),cnt=1,val=b%p;
hash[val]=0;//b*(a^j) mod p,当j=0时的情况
for(int j=1;j<=t;j++){
cnt=(cnt*a)%p;//计算a^t,方便下面用
val=(val*a)%p;//计算b*(a^j)
hash[val]=j;
}
val=1;
for(int i=1;i<=t;++i){
val=(val*cnt)%p;
//此时cnt=a^t,这里相当于计算(a^t)^i
if(hash[val]){
LL ans=(t*i-hash[val]+p)%p;
return ans;
}
}
return -1;
}
模板题,没有任何改动.不放代码了.
题意:给定整数\(K\)和质数\(m\),求最小的正整数\(N\),使得 $ 11\cdots1$(N个1) \(\equiv K \pmod m\)
分析:x个1可以表示为\((10^x-1)/9\),所以本题实际上是求最小正整数x,使得\(10^x\equiv9*k+1(\mod m)\),就是BSGS的板子题了.
但是本题数据毒瘤,最后两个点卡到怀疑人生,直接乘爆long long,写龟速乘真的就龟速了,用__int128还必须要写快读快写,原因好像是我的板子里的map太慢,这里有个链式前向星hash的大佬,戳这里
我直接偷懒__int128+快读快写了.
// luogu-judger-enable-o2
#include <bits/stdc++.h>
#define int __int128
using namespace std;
inline int read() {
int s=0,w=1; char ch=getchar();
while (ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while (ch>='0'&&ch<='9')s=(s<<3)+(s<<1)+ch-'0',ch=getchar();
return s*w;
}
inline void write(int x) {
int sta[30],top=0;
while (x) sta[++top]=x%10,x/=10;
while (top) putchar(sta[top--]+'0');
}
inline int ksm(int a,int b,int c){
int cnt=1;
while(b){
if(b&1)cnt=(cnt*a)%c;
a=(a*a)%c;
b>>=1;
}
return cnt;
}
inline int BSGS(int a,int b,int p){
map<int,int>hash;hash.clear();
b%=p;int t=(int)sqrt((double)p)+1;
for(int j=0;j<t;j++){
int val=b*ksm(a,j,p)%p;
hash[val]=j;
}
a=ksm(a,t,p);
if(a==0)return (b==0)?1:-1;
for(int i=0;i<=t;i++){
int val=ksm(a,i,p);
int j=hash.find(val)==hash.end()?-1:hash[val];
if (j>=0&&i*t-j>=0) return i*t-j;
}
return -1;
}
signed main() {
int k=read(),m=read();
int ans=BSGS(10,(9*k+1)%m,m);
write(ans);
return 0;
}
依旧模板题,需要特判几个地方
if(a%p==0){
printf("Couldn't Produce!\n");
continue;
}
if(b==1){
puts("0");
continue;
}
如果掌握了扩展欧几里得和BSGS,这里有道模板题