BSGS算法(学习笔记)

给定整数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;
}

[TJOI2007]可爱的质数

模板题,没有任何改动.不放代码了.

多少个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;
}

New Product

依旧模板题,需要特判几个地方

    if(a%p==0){
        printf("Couldn't Produce!\n");
        continue;
    }
    if(b==1){
        puts("0");
        continue;
    }

如果掌握了扩展欧几里得和BSGS,这里有道模板题

猜你喜欢

转载自www.cnblogs.com/PPXppx/p/10584804.html