[BZOJ2219]数论之神(BSGS)

题面

http://darkbzoj.tk/problem/2219

题解

前置知识

  • BSGS算法: https://www.cnblogs.com/xh092113/p/12255049.html
  • 求原根: https://blog.csdn.net/zhelong3205/article/details/78366476

我们要求方程\(x^a = r({\mod M})\)(其中M是质数)在模M意义下的解x的数量。

首先将M的素因数分解式写出:\(M = {\prod_{i=1}^{k}}{p_i^{s_i}}\),其中\(p_1为奇素数,\(s_i{\in}Z^*\)

那么我们可以将原方程分解为多个方程:

\[\begin{cases} x^a {\equiv} r {\mod {p_1}^{s_1}} \\… \\x^a {\equiv} r {\mod {p_k}^{s_k}} \end{cases}\]

由中国剩余定理和乘法原理,这第一个方程在模\({p_1}^{s_1}\)意义下的解数\(A_1\)、……第k个方程在模\({p_k}^{s_k}\)意义下的解数\(A_k\)的乘积就是原方程的解数。

因此,现在只需要解决一个形如\(x^a{\equiv}r{\mod p^s}\)的方程在模\(p^s\)意义下的解数的问题。

不妨设\(r {\in} [0,p^s)\)。可以分成三种情况讨论:

  • 1、\(r=0\):这代表着只要\(p^{{\lceil}{\frac{s}{a}}{\rceil}} | x\)就满足方程了,解数为\(p^{s-{\lceil}{\frac{s}{a}}{\rceil}}\)

  • 2、\(r {\neq} 0\),设\(r=p^t*u\),其中\((u,p)=1\)

    • 2.1、\(t=0\):那么x没有约数p。

      \[x^a{\equiv}u{\mod p^s}\]

      设g为\(p^s\)的原根,再设\(m,n{\in}[0,\phi(p^s))\)使得\(x=g^m,u=g^n\)。(由于u,x均与p互质,所以满足条件的m,n一定存在)n可以通过BSGS算法求出,而我们想要求的答案转化为

      \[am{\equiv}n{\mod {\phi(p^s)}}\]

      在模\({\phi(p^s)}\)意义下的解数。那么取\(d=(a,{\phi(p^s)})\),若\(d{\nmid}n\)则无解,否则由裴蜀定理,恰有d组解。

    • 2.2、\(t{\neq}0\):那么此时x中含p的次数已经被确定。如果\(a{\nmid}t\)则无解;否则,确定了有\(v_p(x)={\frac{t}{a}}\)。那么设\(x=p^{\frac{t}{a}}*v ((v,p)=1)\),有

      \[v^a {\equiv} u {\mod p^{s-t}}\]

      此时,方程转为了2.1的形式,可以用2.1的方法,计算出此处v在模\(p^{s-t}\)意义下的解数N。

      由于\(x=p^{\frac{t}{a}}*v\),那么x在模\(p^s\)意义下的解数即为v在模\(p^{s-{\frac{t}{a}}}\)意义下的解数,也就是\(N*p^{t-{\frac{t}{a}}}\)

代码

#include

using namespace std;

#define ll long long
#define rg register 

inline ll read(){
    ll s = 0,ww = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){if(ch == '-')ww = -1;ch = getchar();}
    while('0' <= ch && ch <= '9'){s = 10 * s + ch - '0';ch = getchar();}
    return s * ww;
} 

inline void write(ll x){
    if(x < 0)putchar('-'),x = -x;
    if(x > 9)write(x / 10);
    putchar('0' + x % 10);
}

ll pn;
bool isp[32000+5];
ll pri[32000+5];

inline void Eular(){
    pn = 0;
    for(rg ll i = 2;i <= 32000;i++)isp[i] = 1;
    for(rg ll i = 2;i <= 32000;i++){
        if(isp[i])pri[++pn] = i;
        for(rg ll j = 1;i * pri[j] <= 32000;j++){
            isp[i * pri[j]] = 0;
            if(i % pri[j] == 0)break;
        }
    }
}

inline ll power(ll a,ll n,ll mod){
    ll s = 1,x = a;
    if(!mod){
        while(n){
            if(n & 1)s *= x;
            x *= x;
            n >>= 1;
        }
        return s;
    }
    while(n){
        if(n & 1)s = s * x % mod;
        x = x * x % mod;
        n >>= 1;
    }
    return s;
}

ll k;
ll p[30+5],s[30+5],P[30+5];

inline void divide(ll M,ll &k,ll *p,ll *s,ll *P){ //素因数分解 
    k = 0;
    ll T = (ll)round(sqrt(M));
    for(rg ll i = 1;i <= pn && pri[i] <= T;i++){
        if(M % pri[i] == 0){
            k++;
            p[k] = pri[i];
            for(P[k] = 1,s[k] = 0;M % p[k] == 0;M /= p[k])P[k] *= p[k],s[k]++;
        }
    }
    if(M != 1){
        k++;
        p[k] = P[k] = M;
        s[k] = 1;
    }
}

inline ll gcd(ll a,ll b){
    return b ? gcd(b,a % b) : a;
}

ll _k;
ll _p[30+5],_s[30+5],_P[30+5];

inline ll calcg(ll p,ll P){ //计算P的原根 
    ll phi = P / p * (p - 1);
    divide(phi,_k,_p,_s,_P);
    for(rg ll g = 2;;g++){
        bool b = 1;
        for(rg ll i = 1;i <= _k;i++)if(power(g,phi/_p[i],P) == 1){
            b = 0;
            break;
        }
        if(b)return g;
    }
}

unordered_mapHash;

inline ll BSGS(ll g,ll u,ll phi,ll P){ //计算方程g^x=u(mod P)在模phi(P)意义下的解x 
    Hash.clear();
    ll T = (ll)ceil(sqrt(P));
    ll g_T = power(g,T,P);
    for(rg ll cur = u * g % P,i = 1;i <= T;i++,cur = cur * g % P)Hash[cur] = i;
    for(rg ll cur = g_T,i = 1;i <= T;i++,cur = cur * g_T % P)
    if(Hash.count(cur))return i * T - Hash[cur];    
}

inline ll solve(ll a,ll u,ll p,ll P){ //计算方程x^a=u(mod P)在模P意义下解x的个数 
    ll g = calcg(p,P);
    ll phi = P / p * (p - 1);
    ll n = BSGS(g,u,phi,P); 
    ll d = gcd(a,phi);
    if(n % d)return 0;
    else return d;
}

inline ll calc(ll a,ll r,ll p,ll s,ll P){ //计算各A[i]。P=p^s 
    r %= P;
    if(!r)return power(p,s - (ll)ceil(1.0*s/a),0);
    ll t = 0,u;
    while(r % p == 0)r /= p,P /= p,t++; 
    u = r;
    if(!t)return solve(a,u,p,P);
    else{
        if(t % a)return 0;
        else return power(p,t - t / a,0) * solve(a,u,p,P); //此处的P已经变为p^(s-t)    
    }
}

int main(){
    Eular();
    ll T = read();
    while(T--){
        ll a = read(),r = read(),M = read() * 2 + 1;
        divide(M,k,p,s,P);
        ll ans = 1;
        for(rg ll i = 1;i <= k;i++)ans *= calc(a,r,p[i],s[i],P[i]); 
        write(ans),putchar('\n');
    }
    return 0;
}

你可能感兴趣的:([BZOJ2219]数论之神(BSGS))