BSGS算法!

 求解关于x的方程

a^x=z(mod p),其中gcd (a,p)=1.
 
做法的话并不难,但是要搞懂细节还蛮多的。
 
bsgs算法是这样的:x可以写成i*m-j的形式(这里m取值随意,但是取√p上取整时跑的最快)
  a^(im-j)≡z(mod p)
  推得  a^im≡z*(a^j)
那么我们枚举j的值从0--(m-1),将算出的z*(a^j)加入map里面
再枚举i从1-m,将每次算出的a^im的结果去map中匹配
若匹配到,x=i*m-j。
 
细节
1.i的范围从1到m
证明:

a ^ x Ξ b (mod p) 

a ^ (p-1) Ξ 1 (mod p)

若x大于等于p-1

则a^xΞa^(p-1) * a^(x-p+1)Ξa^(x-p+1)

故x小于p-1

又m=√p

故i从1到m

得证

2.为什么bsgs在跳步过程中没有漏解

原因:在枚举i的过程中,看似是跳过了好多情况,但是其实在计算了每一个i之后,都会去map里面找每一个j去对应,相当于是将每一个i所对应的余数(剩余系)都枚举了一遍,即枚举了一遍完整的x的范围(因为x=i*m-j)。

 
洛谷p2485  计 算器
bsgs裸题
直接粘代码
 
CODE!
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll t,k,y,z,p;
map<ll,ll>a;
ll fpow(ll a,ll b,ll p)
{
    ll ret=1;
    while(b)
    {
        if(b&1) ret=(ret*a)%p;
        b>>=1;
        a=(a*a)%p;
    }
    return ret;
}
void exgcd(ll a,ll b,ll &gcd,ll &xx,ll &yy)
{
    if(!b)
    {
        xx=1,yy=0;
        gcd=a;
        return;
    }
    exgcd(b,a%b,gcd,xx,yy);
    ll tr=xx;
    xx=yy;
    yy=tr-a/b*yy;
}
int main()
{
    cin>>t>>k;
    if(k==1) 
    {
        while(t--)
        {
            scanf("%lld%lld%lld",&y,&z,&p);
            printf("%lld\n",fpow(y,z,p));
        }
    }
    else if(k==2)
        {
            while(t--)
            {
                scanf("%lld%lld%lld",&y,&z,&p);
                ll gcd,xx,yy;
                exgcd(y,p,gcd,xx,yy);
                if(z%gcd!=0) printf("Orz, I cannot find x!\n");
                else 
                {
                    xx*=z/gcd;
                    xx%=(p/gcd);
                    printf("%lld\n",(xx+(p/gcd))%(p/gcd));
                }
            }
            
        }
        else
        {
            while(t--)
            {
                scanf("%lld%lld%lld",&y,&z,&p);
                ll m=ceil(sqrt(p));
                if(y%p==0&&z) {printf("Orz, I cannot find x!\n");continue;};
                a.clear();
                ll now=z%p,f=fpow(y,m,p);
                a[now]=0;
                for(ll i=1;i<=m;i++)
                {
                    now=(now*y)%p;
                    a[now]=i;
                }
                now=1;
                ll flag=0;
                for(ll i=1;i<=m;i++)
                {
                    now=(now*f)%p;
                    if(a[now])
                    {
                        ll ans=(i*m-a[now])%p;
                        printf("%lld\n",(ans+p)%p);
                        flag=1;
                        break;
                    }
                }
                if(!flag) printf("Orz, I cannot find x!\n");
            }
        }
    return 0;
}

ps.不开long long见祖宗可还行

猜你喜欢

转载自www.cnblogs.com/oierglh/p/11234686.html