bzoj1420 Discrete Root 原根 大步小步 exgcd

题意:模方程 xab(modp)
题目没说。但是此题里p应该为素数。。
1.原根的概念。
对于素数p,如果存在一个正整数 1<a<p ,使得 a1,a2,,ap1 模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) ,若是,则不是原根。
枚举那些 b p1 的所有因子吗?不是。对于 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 long
using 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;
}
发布了87 篇原创文章 · 获赞 7 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/yxr0105/article/details/51644496