zoukankan      html  css  js  c++  java
  • 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;
    }
  • 相关阅读:
    linux 文件搜索
    解决android 无法打开 DDMS 中的data目录
    JAVA 截图+tess4j识别
    JAVA 获取网页源代码保存到本地文件
    java连接sqlserver数据简单操作
    SQL server 2008 安装提示:属性不匹配
    SQLServer 安装提示需要重启计算机的解决方案
    Android蓝牙----打开,关闭操作
    JAVA中String类的比较
    Android中的AlertDialog和ProgressDialog用法
  • 原文地址:https://www.cnblogs.com/zhangleo/p/10969566.html
Copyright © 2011-2022 走看看