bzoj1420 Discrete Root 原根 大步小步 exgcd

来源:互联网 发布:什么是云计算 视频 编辑:程序博客网 时间:2024/04/30 07:08

题意:模方程xab(modp)
题目没说。但是此题里p应该为素数。。
1.原根的概念。
对于素数p,如果存在一个正整数1<a<p模p的值取遍1,2,,p1一一对应且不重复不遗漏。称a是p的一个原根。
根据欧拉定理可知,ap11(mod p),apa1(mod p),ap+1a2(mod p),aiaj(mod p) 等价于 ij(mod (p1))
a1,a2,,ap1模p有循环节,原根的循环节长度为p1,不是原根的循环节长度为p1的真因子。
2.原根的求法。
p的原根有ϕ(p1)个。有很多。所以枚举即可 。对于每个枚举的数,再枚举循环节长度b,判断是否有mb1(mod p),若是,则不是原根。
枚举那些bp1的所有因子吗?不是。对于p1含有的所有质因子pr[i]我们只需要枚举p1pr[i]即可。因为循环节长度为a的,ka也一定为循环节。
3.模方程求解。
如果找到了p的一个原根m,设x=my,a=mz,求解z变成了求a在模p意义下的离散对数。则模方程变为maymz(mod p),根据1里的结论,ayz(mod p1)这个线性模方程用exgcd求解。
依然有很多细节:
1、1和2没有原根,特判。
2、b=0特判。因为没有解。

//x^a = b (mod p)//1,2 没有原根,特判。 #include<cstdio>#include<cmath>#include<map>#include<algorithm>#define ll long longusing namespace std;ll p;int cnt;ll pr[1000005];ll pow(ll a,ll b,ll p){    ll ret=1;    while(b){        if(b&1) ret=ret*a%p;        a=a*a%p;b>>=1;    }    return ret;}ll inv(ll a,ll p){return pow(a,p-2,p);}void getprime(ll now){    for(int i=2;i*i<=now;i++)        if(now%i==0){            pr[++cnt]=i;            while(now%i==0) now/=i;        }    if(now!=1) pr[++cnt]=now;    //for(int i=1;i<=cnt;i++) printf("%lld ",pr[i]);puts("");}bool judge(ll x){    for(int i=1;i<=cnt;i++)        if(pow(x,(p-1)/pr[i],p)==1) return 0;    return 1;}//若a为循环节,ka也为循环节 ll getgen(){    for(ll i=2;;i++)        if(judge(i)) return i;}map<ll,ll>hash;ll BSGS(ll a,ll b,ll p){    hash.clear();    int m=(int)sqrt(p)+1;    ll e=1;hash[e]=0;    ll v=inv(pow(a,m,p),p);    for(int i=1;i<m;i++){        e=e*a%p;        if(!hash.count(e)) hash[e]=i;    }    for(int i=0;i<=m;i++){        if(hash.count(b)) return i*m+hash[b];        b=b*v%p;    }    return -1;}void exgcd(ll a,ll b,ll &d,ll &x,ll &y){    if(!b) {x=1;y=0;d=a;return;}    exgcd(b,a%b,d,y,x);y-=x*(a/b);}map<ll,bool>vis;ll ans[1000005];int main(){    freopen("in.txt","r",stdin);    freopen("out.txt","w",stdout);    ll a,b,m,counter=0;    scanf("%lld%lld%lld",&p,&a,&b);b%=p;    if(b==0) return puts("1\n0"),0;    if(p==2) {        if(b==1) puts("1\n1");        else puts("1\n0");        return 0;    }    if(p==3){        for(int i=0;i<=2;i++)            if(pow(i,a,p)==b) counter++;        printf("%lld\n",counter);        for(int i=0;i<=2;i++)            if(pow(i,a,p)==b) printf("%d\n",i);        return 0;    }     getprime(p-1);    m=getgen();    ll z=BSGS(m,b,p);    ll d,x,y;    exgcd(a,p-1,d,x,y);    if(z%d!=0) return puts("0"),0;    ll t=(p-1)/d;    x=x*(z/d)%t;x=(x%t+t)%t;    for(ll tt=pow(m,x,p);!vis.count(tt);x+=t,tt=pow(m,x,p)) vis[tt]=1,ans[++counter]=tt;    printf("%lld\n",counter);    sort(ans+1,ans+1+counter);    for(int i=1;i<=counter;i++) printf("%lld\n",ans[i]);    return 0;}
0 0