ADP+Pi==ATP

苦しみも涙も 越えて行くって

求解离散对数——BSGS算法の板子

BSGS这玩意儿好像用的不是很多?好像除了POJ和HDU有两道题,然后就是SDOI的两个题了。。然后关于这玩意儿正确性的很多证明ATP都不是很懂。。这里只是解释一下它的实现过程。。

BSGS

BSGS算法(Baby Step Gaint Step)是用于求解离散对数(即ALB(mod C)中的最小的L)的一种算法。它的实质是一种优化的枚举算法,但是它通过数学分析缩小了枚举的范围,一般在O(n)O(nlogn)(如果使用STL的map作为哈希表)的时间复杂度内就能出解。

普通的BSGS算法要求A,C互质。这里先贴板子再解释。。。(其中powww(a,t,Mod)是计算快速幂at % Mod的过程)

void BSGS(LL A,LL B,LL C){
    LL m=ceil(sqrt(C)),k=B%C,am,x;
    bool end=false;
    if (B%C==0){
        printf("No Solution\n");
        return;
    }
    hash.clear();
    hash[k]=0;
    am=powww(A,m,C);
    for (int j=1;j<=m;j++){
        k=k*A%C;hash[k]=j;
    }
    x=1;
    for (int i=1;i<=m;i++){
        x=x*am%C;
        if (hash[x]!=0){
            printf("%I64d\n",((i*m-hash[x]+cnt)%C+C)%C);
            end=true;
            return;
        }
    }
    if (end==false)
      printf("No Solution\n");
}

对于要求的式子ALB(mod C),首先我们设定一个值M,并且设L=iMj。那么原来的式子就能表示成AiMjB。显然我们可以把Aj移到等式的右侧变成AiMBAj。那么观察有多少个“本质不同”的j,这里的两个“本质不同”指的是它不能通过移项变成另外一个具有AiMBAj形式的式子。

比如说,j=1j=M+1这两个就是本质相同的,因为如果取j=M+1的话,式子就会变成AiMBAM+1,把AM移到左边去就变成了A(i1)MBA1,也就是说它和j=1是等价的。

那么我们可以发现,本质不同的j只能是0..M1这些取值,一共有M个。那么对应的i也一共只有M种情况。如果我们能够把i,j都求出来那么这个问题也就解决了。我们可以把这M种情况所算出来的BAj都用哈希表映射到j上。

接下来我们就要寻找能够让式子有解的i了。这里有一点就是要尽量保证i最小,因为i为答案贡献的值是iM,如果能够保证i比较小,就算j比较大也没有关系,因为j不会超过M。那么就从1到CM(C是模数)枚举i,算出AiM以后在哈希表里查,如果能查到合法的j,那么iMj就是要求的最小解。如果枚举完了所有的i都没有找到合法的j,就说明这个式子无解。

一般地,我们令M=C。(这玩意儿好像有个证明?然而愚蠢的ATP并不会。。)

这里还有一个问题:话说回来上面提到过本质不同的j只能是0..M1,那我上面枚举的时候明明枚举到M了啊。。这里实际上是STL的map的锅,因为map里面没有东西的元素都是0,那如果我又把某个元素赋值成0的话就没法区分它到底是有没有东西。。也就是说如果当前最小的i对应的j恰好等于0,它就会认为map里面没有东西然后给判过去,这是不应该的。。而解决办法就是增加一个j=M,这样的话就能正确判断出答案恰好是M的倍数的情况。

网上还有一种写法是设L=iM+j,正确性也是一样的,但是操作的时候需要用到逆元。

板子题:POJ2417(BZOJ3239)/BZOJ2242/BZOJ3122

扩展算法

上面说过,BSGS算法要求A,C互质,那么如果A,C不互质怎么办呢?那么就要用到BSGS的扩展算法了。扩展算法的原理就是通过提取公因数使得A,C互质,然后再用普通的BSGS求解。

继续贴板子:

void ex_BSGS(LL A,LL B,LL C){
    int t=ceil(log2(C));
    flag=false;
    for (int i=0;i<=t;i++)
      if (powww(A,i,C)==B){
         printf("%d\n",i);
         flag=true;
         break;
      }
    if (flag) continue;
    cnt=0;P=1;
    d=gcd(A,C);
    while (d!=1){
        ++cnt;
        if (B%d!=0){
            printf("No Solution\n");
            flag=true;break;
        }
        B/=d;C/=d;P*=d;
        d=gcd(A,C);
    }
    if (flag) continue;
    exgcd(inv,y,P,C);
    inv=((inv%C)+C)%C;
    inv1=powww(A,cnt,C)*inv%C;
    exgcd(inv,y,inv1,C);
    inv=((inv%C)+C)%C;
    B=B*inv%C;ans=0;
    BSGS(A,B,C);
}

首先要想使A,C互质,我们就要提取A和C的最大公约数。假设这个公约数是d,提取完了以后就变成了AdAL1Bd(mod Cd)。如果B不能整除d,说明这个式子是无解的;那么现在新的C变成了Cd,如果AC互质,就能直接用BSGS求解了,否则继续上面的过程,不断提取最大公因数。假设提取了t个最大公因数,它们的乘积为tmp,那么最后的式子就变成了AttmpALtBtmp(mod Ctmp)。这个时候ACtmp是互质的,用逆元把tmp移到等式右边就可以用BSGS求解了。

上面一堆都是ATP口胡的。。。zyf2000那里有更加严谨和详细的证明。。
板子题:BZOJ1467/HDU2815

阅读更多
版权声明:本文为博主口胡,请谨慎转载 https://blog.csdn.net/FromATP/article/details/56013530
文章标签: BSGS 数论
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

不良信息举报

求解离散对数——BSGS算法の板子

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭