zoukankan      html  css  js  c++  java
  • 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^jmod p)的值,并插入哈希表或者map中.

    然后枚举i的所有取值((0<=i<=t)),同样可以计算出((a^t)^imod 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),使得 $ 11cdots1$(N个1) (equiv K pmod m)

    分析:x个1可以表示为((10^x-1)/9),所以本题实际上是求最小正整数x,使得(10^xequiv9*k+1(mod m)),就是BSGS的板子题了.

    但是本题数据毒瘤,最后两个点卡到怀疑人生,直接乘爆long long,写龟速乘真的就龟速了,用__int128还必须要写快读快写,原因好像是我的板子里的map太慢.

    所以我学会了链式前向星hash和神仙O(1)快速乘.

    #include<bits/stdc++.h>
    #define LB long double
    #define LL long long
    using namespace std;
    struct Hash{
        LL p,x,y;
    }b[376637];
    LL tot,head[376637];
    inline LL read(){
        LL 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*10+ch-'0';ch=getchar();}
        return s*w;
    }
    inline LL ksc(LL x,LL y,LL p){
        return (x*y-(LL)((LB)x/p*y)*p+p)%p;
    }
    inline void hash(LL x,LL y){
        b[++tot].x=x;b[tot].y=y;y%=376637;
        b[tot].p=head[y];head[y]=tot;
    }
    inline LL BSGS(LL x,LL y,LL p){
        LL z=sqrt(p)+1,sx=1,sy=1;
        for(int i=0;i<z;i++){
    		sx=ksc(sx,x,p);
    		hash(i,ksc(sx,y,p));
        }
        sy=sx;
        for(int i=1;i<=z;++i,sy=ksc(sy,sx,p)){
            for(int j=head[sy%376637];j;j=b[j].p)
                if(sy==b[j].y)return (LL)i*z-b[j].x;
        }
    }
    int main(){
        LL k=read(),m=read();k=(k*9+1)%m;
        printf("%lld
    ",BSGS(10,k,m));
        return 0;
    }
    
    

    New Product

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

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

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

  • 相关阅读:
    函数part1 初始函数
    文件操作 文件修改的两种方式
    文件操作part3 文件内指针的移动
    文件操作part2
    day08作业
    文件操作part1
    字符编码
    ORA-15124 数据库启动阶段报错
    将数据库从普通文件系统迁移到ASM中
    在ASM中移动数据文件
  • 原文地址:https://www.cnblogs.com/PPXppx/p/10584804.html
Copyright © 2011-2022 走看看