• BSGS详析


    BSGS是Baby-step-Giant-step的简写,是用来求解一类问题的算法

    模型:求解方程$A^{x}equiv B$ mod $C$,保证C为质数

    首先,我们分析一下暴力的方法:直接从0开始枚举x计算,直到求出答案为止

    时间复杂度?

    $O(C)$

    等等,为什么是$O(C)$?

    根据费马小定理,$A^{C-1}equiv 1$mod $C$,因此仅需枚举$xin [0,C-1]$即可找出所有可能的结果,比较即可

    这个复杂度不够优秀,有没有什么方法能降下来?

    分块!

    设$m=sqrt{C}$,那么每一个$x$都可以写成$x=i*m+j$的形式,由于有$x<C$,因此$iin [0,m],jin [0,m]$!

    优化在哪了?

    我们发现,如果把$x$变成这个形式,那么我们就可以对原表达式进行变形:

    $A^{x}equiv B$ mod $C$

    $A^{i*m+j}equiv B$ mod $C$

    $A^{i*m}*A^{j}equiv B$ mod $C$

     乱七八糟鼓捣了半天,这是要干啥?

    我们发现,$A^{i*m}$这一项的取值不超过$sqrt{C}$个,因此我们可以将之预处理出来,同时$A^{j}$的取值也不超过$sqrt{C}$个,同样可以预处理,而且我们发现,如果将$A^{i*m}$这一项作为已知的系数进行枚举,那么这就变成了一个同余方程,用ex_gcd或费马小定理求解是可以在$O(log2C)$(大概是这个复杂度)的时间内求出对应的$A^{j}$的!

    于是我们仅需检验这一$A^{j}$是否出现过即可,借助map或hash表,这一过程可以被简化成$O(log)$或$O(1)$的!

    于是总复杂度即为$O(sqrt{C}log2(C))$

    贴代码

    (这个是bzoj 2242的代码,这是一道板子题,前20分是快速幂,中间35分是ex_gcd或者费马小定理,最后45分是BSGS)

    #include <cstdio>
    #include <cmath>
    #include <map>
    #define ll long long
    using namespace std;
    ll y,z,p;
    ll mul[200005];
    map <ll,int> M;
    int T,k;
    ll gcd(ll a,ll b)
    {
        return b?gcd(b,a%b):a;
    }
    ll pow_mul(ll a,ll b,ll mod)
    {
        ll ret=1;
        while(b)
        {
            if(b&1)ret=ret*a%mod;
            a=a*a%mod,b>>=1;
        }
        return ret;
    }
    int main()
    {
        scanf("%d%d",&T,&k);
        while(T--)
        {
            scanf("%lld%lld%lld",&y,&z,&p);
            if(k==1)printf("%lld
    ",pow_mul(y,z,p));
            else if(k==2)
            {
                if(gcd(y,p)!=1)printf("Orz, I cannot find x!
    ");
                else printf("%lld
    ",pow_mul(y,p-2,p)*z%p);
            }else
            {
                M.clear();
                if(z==1){printf("0
    ");continue;}
                ll tt=(ll)sqrt(p)+1;
                ll ed=0;
                for(ll i=0;i*tt<=p;i++)mul[i+1]=pow_mul(y,i*tt,p),ed=i;
                for(ll i=0;i<=tt;i++)M[pow_mul(y,i,p)]=i+1;
                bool flag=0;
                for(ll i=0;i<=ed;i++)
                {
                    if(gcd(mul[i+1],p)!=1)continue;
                    ll temp=pow_mul(mul[i+1],p-2,p)*z%p;
                    if(M[temp]){printf("%lld
    ",M[temp]-1+i*tt);flag=1;break;}
                }
                if(!flag)printf("Orz, I cannot find x!
    ");
            }
        }
        return 0;
    }
  • 相关阅读:
    HTTP协议实体的基本讲解
    了解Xcode目录结构 开发者必看
    图解oracle 之从客户端到服务端的sql追击
    iPhone开发经典语录集锦
    iphone网络编程 http请求 get / post(发送二进制文件) 总结
    iPhone 路径大全
    http发送文件的原理
    iPhone开发面试题葵花宝典
    来自mooon的最简单的日志类CSimpleLogger
    不再担心日志文件过大:通用日志滚动脚本
  • 原文地址:https://www.cnblogs.com/zhangleo/p/10969566.html
Copyright © 2020-2023  润新知