BSGS+原根+离散对数

前提知识:欧拉定理

这个东西有点强啊。

离散对数就是求给定a,b,p
让你求 a^x ≡b mod(p) 意义下的 这个x。

这个东西我们用BSGS算法有点牛逼的名字(拔山盖世,北上广深)

我们令x=i*c+j , c=ceil(sqrt(p)) 也就是根号p向上取整。
可以证明如果x有解,那么x一定小于p因为根据欧拉定理,
a^φ[p] ≡ 1 Mod(p)
而φ[p]是一定小于p的所以如果有解那么解一定在p之内,
所以可以知道i一定小于c,j也小于c。
那么就有
a^(i*c+j) ≡ b mod(p)

我们枚举i,那么我们的目标就是能否找到一个j使得原来的式子成立,我们想到了什么,没错就是hash,我们最开始存入每一个a^j次方一直到c,因为j的范围为c。那么我们的目标就是看看hash表里有没有一个这样的数能让原来的式子成立,这个数我们算算出来就是
a^i 在 mod(p)意义下的逆元乘上b,然后查一查就好了。
总时间复杂度O(sqrt(p))

代码解释说明:
这份代码是来求这样一个问题的:
这里写图片描述

这个要用到一个式子我用ind(x)表示在原根g下mod (p)的离散对数
x=g^ind(x) mod(p)
下面我会说明,求离散对数的方程我在代码里标注了。
代码如下:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<map>
#include<algorithm>
#include<cmath>
#define N 100005
#define PN "modlog"
#define ll long long
#define inf 0x7fffffff
#define S 100007
using namespace std;
struct Hash{
    int head[S],dest[S][2],last[S],etot;
    void init(){
        memset(head,0,sizeof(head));
        etot=0;
    }
    void add(int a,int b){
        int key=a%S;
        for(int t=head[key];t;t=last[t])
            if(dest[t][0]==a)return;
        etot++;
        dest[etot][0]=a;
        dest[etot][1]=b;
        last[etot]=head[key];
        head[key]=etot;
    }
    int find(int a){
        int key=a%S;
        for(int t=head[key];t;t=last[t])
            if(dest[t][0]==a)return dest[t][1];
        return -1;
    }
}hash;
ll x,y,prime[N],ptot,isnot[N],t,type,a,b,p,g,ppp;
void pre(){
    for(ll i=2;i<N;i++){
        if(isnot[i]==0)
            prime[++ptot]=i;
        for(ll k=1;k<=ptot&&prime[k]*i<N;k++){
            isnot[prime[k]*i]=1;
            if(i%prime[k]==0)break;
        }
    }
}
ll exgcd(ll a,ll b,ll &x,ll &y){
    if(b==0){
        x=1;y=0;
        return a;
    }
    ll d=exgcd(b,a%b,x,y);
    ll t=x;
    x=y;
    y=t-a/b*y;
    return d;
}
ll ipow(ll a,ll b,ll p){
    ll rt=1;
    for(;b;b>>=1,a=a*a%p)
    if(b&1) rt=rt*a%p;
    return rt;
}
bool jfc(ll a,ll b,ll c){
    ll d=exgcd(a,b,x,y);
    if(c%d)return false;
    ll t=b/d;
    if(t<0)t=-t;
    ppp=t;
    x*=(c/d);
    x=(x%t+t)%t;
    return true;
}
ll inverse(ll a,ll m){
    ll d=exgcd(a,m,x,y);
    if(d!=1)return -1;
    return (x%m+m)%m;
}
ll ind(ll a,ll b,ll m){//求离散对数的函数
    ll c=(ll)ceil(sqrt(m)+1);
    hash.init();
    ll cur=1;
    for(ll i=0;i<c;cur=cur*a%m,i++){
        hash.add(cur,i);
        if(b==cur)return i;
    }
    ll base=inverse(cur,m);
    cur=base*b%m;
    for(ll i=c;i<=m;i+=c,cur=cur*base%m){
        int j=hash.find(cur);
        if(j!=-1)return i+j;
    }
    return -1;
}
ll get_g(ll m){//求原根
    ll p=m-1;
    for(ll i=2;i<m;i++){
        ll k;
        for(k=1;k<=ptot&&prime[k]*prime[k]<=p;k++)
        if(p%prime[k])continue;
        else
            if(ipow(i,p/prime[k],m)==1)break;
        if(prime[k]*prime[k]>p)return i;
    }
}
int main(){
    freopen(PN".in","r",stdin);
    freopen(PN".out","w",stdout);
    pre();
    cin>>t>>type;
    while(t--){
        cin>>a>>b>>p;
        g=get_g(p);
        ll indb=ind(g,b,p),k=p-1;
        if(indb==-1&&b==0){
            if(type==1)
            printf("1 0\n");
            else
            printf("1\n");
            continue;
        }
        if(jfc(a,k,indb)==0){
            if(type==1)
            printf("0 0\n");
            else printf("0\n");
            continue;
        }
        else{
            ll ans=(k-x)/ppp+1;
            if((k-x)%ppp==0)ans--;
            ll y=ans-1;
            ll q1=ipow(g,ppp,p);
            ll bg=ipow(g,x,p);
            ll ans1=ipow(q1,y+1,p);
            ans1--;
            ll ttt=inverse(q1-1,p);
            ans1=ans1*ttt%p;
            if(y==0)ans1=1;
            ans1=ans1*bg%p;
            if(type==1)
            cout<<ans<<' '<<ans1<<endl;
            else
            cout<<ans<<endl;
        }
    }
}

Definition (阶)
给定一个与m(1 m) 互素的a, 则最小的一个满足:
a^r ≡ 1 (mod m)
的正整数r 叫做a 模m 的阶, 这里记作r =阶 (a)(打不出特殊字符)
Definition (元根)
对于模数m, 如果存在一个数g, 满足:
阶(g) = φ(m)
我们则称g 为模m 的一个元根

套用的pdf上的说明0 0

原根有一个很牛的性质,他在mod (m)意义下的各种次方,可以表示完m的缩系里的所有数,也就是φ(m)里的所有数。
那么我们求出它就可以来干很牛逼的事情。可以参考上面的题目。
首先 有个定理欧拉定理是a^φ[p] ≡ 1 Mod(p)
求原根我们枚举数字a(汗没错就是枚举)因为原根一般都很小,从小到大枚举,然后我们用(φ(m))去除以它的每个质因子得到的数记为qi,判断a^qi 是否mod m为1 如果是那么这个枚举的a就不行,否则我们判断完所有的质因子都mod m不为1那么就可以。
原因:因为比φ(m)都小的数包含了其他所有质因子mod m都不为1那么阶就是φ(m)。
上面代码里有求原根。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值