BZOJ 1420 Discrete Root

思路:数学大汇总

提交:\(3\)

错因:有一个\(j\)写成\(i\)

题解:

求:\(x^k \equiv a \mod p\)
我们先转化一下:求出\(p\)的原根\(g\)
然后我们用\(BSGS\)可以求出 \(g^b \equiv a \mod p\),即\(a\)的指标\(b\).然后因为原根的幂可以表示\([0,p-1]\)中的任何一个数,所以设\(x=g^y\),原式可以转化成 \((g^y)^k \equiv a \mod p\),即\(g^{y*k} \equiv g^b\mod P\),所以指数满足 \(y*k \equiv b \mod \varphi(p)\),可以用\(exgcd\)解出这个方程的解。

代码:

#include<cstdio>
#include<iostream>
#include<cmath>
#include<map>
#include<algorithm>
#define ll long long
#define RR register ll
#define int ll
#define R register int
using namespace std;
namespace Luitaryi {
template<class I> inline I g(I& x) { x=0; register I f=1;
    register char ch; while(!isdigit(ch=getchar())) f=ch=='-'?-1:f;
    do x=x*10+(ch^48); while(isdigit(ch=getchar())); return x*=f;
} ll a,n,k,m,p,G,cnt,mem[100010],ind,ans[100010];
map<ll,ll> mp;
inline ll qpow(ll a,ll b) { RR ret=1;
    for(;b;b>>=1,(a*=a)%=p) if(b&1) (ret*=a)%=p; return ret;
}
inline void exgcd(ll a,ll b,ll& x,ll& y) {
    if(!b) {x=1,y=0; return ;} 
    exgcd(b,a%b,y,x); y-=a/b*x; 
}
inline ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
inline int BSGS() {
    R t=sqrt(p)+1; RR sum=a;//BSGS求a的指标 
    for(R i=1;i<=t;++i) (sum*=G)%=p,mp[sum]=i; 
    sum=qpow(G,t); if(sum==0) return a==0?1:-1; 
    for(R i=1;i<=t;++i) {
        RR tmp=qpow(sum,i);
        if(mp.count(tmp)&&i*t-mp[tmp]>=0) return i*t-mp[tmp];
    } return -1;
}
inline void main() {
    m=g(p)-1,g(k),g(a); if(!a) return (void) printf("1\n0\n"); RR x=m;
    for(R i=2,lim=sqrt(m);i<=x&&i<=lim;++i) {
        if(x%i==0) mem[++cnt]=i;
        while(x%i==0) x/=i;
    } if(x>1) mem[++cnt]=x; 
    //求原根 
    for(R i=2;i<=m;++i) { register bool flg=true;
        for(R j=1;j<=cnt;++j) {
            if(qpow(i,m/mem[j])==1) {
                flg=false; break;
            } 
        } if(flg) {G=i; break;}
    } 
    //求指标 
    ind=BSGS(); if(ind==-1) return (void) puts("0"); 
    //解同余方程 
    RR y; RR d=gcd(k,m);
    if(ind%d) return (void) puts("0");
    R A=k/d,B=m/d; ind/=d; 
    exgcd(A,B,x,y); (x*=ind)%=m; x=(x%B+B)%B;
    cnt=0;
    while(x<=m) ans[++cnt]=qpow(G,x),
    x+=B; sort(ans+1,ans+cnt+1);
    printf("%lld\n",cnt); for(R i=1;i<=cnt;++i) printf("%lld ",ans[i]);
}
} signed main() {Luitaryi::main(); return 0;}

2019.08.22
78

转载于:https://www.cnblogs.com/Jackpei/p/11393645.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值