51nod1123 X^A Mod B (任意模数的K次剩余)

Posted sigongzi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了51nod1123 X^A Mod B (任意模数的K次剩余)相关的知识,希望对你有一定的参考价值。

题解

K次剩余终极版!orz
写一下,WA一年,bug不花一分钱

在很久以前,我还认为,数论是一个重在思维,代码很短的东西
后来。。。我学了BSGS,学了EXBSGS,学了模质数的K次剩余……代码一个比一个长……
直到今天,我写了240行的数论代码,我才发现数论这个东西= =太可怕了

好吧那么我们来说一下任意模数的K次剩余怎么搞
首先,如果模数是奇数,我们可以拆成很多个质数的指数幂,再用解同余方程的方法一个个合起来,细节之后探讨

但是如果,模数有偶数呢
因为要输出所有解,解的个数不多,我们可以倍增,也就是如果模数有一个\(2^k\)因子,那么它在模\(2^{k - 1}\)情况下的所有解x,如果还要在\(2^{k}\)成立,必定是\(x\)\(x + 2^{k - 1}\)
我们对于每一个检查一下就好了

这样,我们就只要考虑模奇数的情况了

对于一个质数的指数幂\(p^{k}\)\(x^{A} \equiv C \pmod {p^{k}}\)
\(C == 0\)
那么\(x\)中必然有\(p^{\lceil \frac{k}{A} \rceil}\)这个因子,之后从0枚举,一个个乘起来就是\(x\)可能的取值

\(C \% p == 0\)
也就是说,\(C\)可以写成\(u * p^{e}\)的形式,有解必定有\(A|e\)
那么就是\(x^{A} \equiv u * p^{e} \pmod {p^{k}}\)
把同余式打开,可以有\(x^{A} = u * p^{e} + h * p^{k}\)
等式两边都除掉一个\(p^{e}\)就有
\((\frac{x}{p^{\frac{e}{A}}})^{A} = u + h * p^{k - e}\)
\(t = \frac{x}{p^{\frac{e}{A}}}\)
我们要求的就是
\(t^{A} \equiv u \pmod {p^{k - e}}\)
这时候\(u\)必然和模数互质,可以套用模质数的K次剩余
此时求出来的指标要取模的数是\(\phi(p^{k - e})\)而不是\(\phi(k)\)
之后求出所有指标的上界是\(\phi(k)\) (就是不断增加\(\frac{\phi(p^{k - e})}{gcd(\phi(p^{k - e},A))}\)的时候)

如果\(C\)\(p\)互质
那么直接上模质数的K次剩余(虽然是质数的指数幂但是你不需要用到有质数的那些位置了)

最后求完了,和上一次的答案用同余方程合起来即可

(附赠51nod上tangjz的hack数据,我虽然ac了然而这个hack没过,又调了一段时间才过掉)
1
3 531441 330750

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <ctime>
//#define ivorysi
#define MAXN 100005
#define eps 1e-7
#define mo 974711
using namespace std;
typedef long long int64;
typedef unsigned int u32;
typedef double db;
int64 A,B,C,G,eu;
int64 ans[MAXN],tmp[MAXN],R,L[MAXN],cntL;
int M;
struct node {
    int next,num;
    int64 hsh;
}E[MAXN];
int head[mo + 5],sumE;
int64 fpow(int64 x,int64 c,int64 MOD) {
    int64 res = 1,t = x;
    while(c) {
    if(c & 1) res = res * t % MOD;
    t = t * t % MOD;
    c >>= 1;
    }
    return res;
}
int primitive_root(int64 P,int64 eu) {
    static int64 factor[1005];
    int cnt = 0;
    int64 x = eu;
    for(int64 i = 2 ; i <= x / i ; ++i) {
    if(x % i == 0) {
        factor[++cnt] = i;
        while(x % i == 0) x /= i;
    }
    }
    if(x > 1) factor[++cnt] = x;
    for(int G = 2 ;  ; ++G) {
    for(int j = 1 ; j <= cnt ; ++j) {
        if(fpow(G,eu / factor[j],P) == 1) goto fail;
    }
    return G;
        fail:;
    }
}
int64 gcd(int64 a,int64 b) {
    return b == 0 ? a : gcd(b,a % b);
}
void ex_gcd(int64 a,int64 b,int64 &x,int64 &y) {
    if(b == 0) {
    x = 1,y = 0;
    }
    else {
    ex_gcd(b,a % b,y,x);
    y -= a / b * x;
    }
}
int64 Inv(int64 num,int64 MOD) {
    int64 x,y;
    ex_gcd(num,MOD,x,y);
    x %= MOD;x += MOD;
    return x % MOD;
}
void add(int u,int64 val,int num) {
    E[++sumE].hsh = val;
    E[sumE].next = head[u];
    E[sumE].num = num;
    head[u] = sumE;
}
void Insert(int64 val,int num) {
    int u = val % mo;
    for(int i = head[u] ; i ; i = E[i].next) {
    if(val == E[i].hsh) {
        E[i].num = num;
        return;
    }
    }
    add(u,val,num);
}
int Query(int64 val) {
    int u = val % mo;
    for(int i = head[u] ; i ; i = E[i].next) {
    if(val == E[i].hsh) {
        return E[i].num;
    }
    }
    return -1;
}
int BSGS(int64 A,int64 C,int64 P) {
    memset(head,0,sizeof(head));sumE = 0;
    int64 S = sqrt(P);
    int64 t = 1,invt = 1,invA = Inv(A,P);
    
    for(int i = 0 ; i < S ; ++i) {
    if(t == C) return i;
    Insert(invt * C % P,i);
    t = t * A % P;
    invt = invt * invA % P;
    }
    int64 tmp = t;
    for(int i = 1 ; i * S < P ; ++i) {
    int x = Query(tmp);
    if(x != -1) {
        return i * S + x;
    }
    tmp = tmp * t % P;
    }
}
bool Process(int64 A,int64 C,int64 P,int k) {
    int64 MOD = 1,g;
    for(int i = 1 ; i <= k ; ++i) MOD *= P;
    cntL = 0;
    if(C % MOD == 0) {
    int64 T = (k - 1) / A + 1;
    L[++cntL] = 0;
    if(T < k) { 
        int64 num = fpow(P,T,MOD);
        for(int i = 1 ; i * num < MOD ; ++i) L[++cntL] = i * num;
    }
    }
    else if(g = gcd(C % MOD,MOD) != 1){
    int64 x = C % MOD;
    int c = 0;
    while(x % P == 0) ++c,x /= P;
    if(c % A != 0) return false;
    G = primitive_root(MOD / (C / x),eu / (C / x));
    eu /= C / x;
    int e = BSGS(G,x,MOD / (C / x));
    g = gcd(A,eu);
    if(e % g != 0) return false;
    e /= g;
    int64 s = Inv(A / g,eu / g) * e % (eu / g);
    L[++cntL] = s;
    while(1) {
        if((L[cntL] + eu / g) % (eu * (C / x)) == L[1]) break;
        L[cntL + 1] = L[cntL] + eu / g;
        ++cntL;
    }
    for(int i = 1 ; i <= cntL ; ++i) {
        L[i] = fpow(G,L[i],MOD) * fpow(P,c / A,MOD) % MOD;
    }
    }
    else {
    int e = BSGS(G,C % MOD,MOD);
    g = gcd(A,eu);
    if(e % g != 0) return false;e /= g;
    int s = Inv(A / g,eu / g) * e % (eu / g);
    L[++cntL] = s;
    while(1) {
        if(L[cntL] + eu / g >= eu) break;
        L[cntL + 1] = L[cntL] + eu / g;
        ++cntL;
    }
    for(int i = 1 ; i <= cntL ; ++i) L[i] = fpow(G,L[i],MOD);
    }
    if(!cntL) return false;
    if(!M) {
    M = cntL;
    for(int i = 1 ; i <= M ; ++i) ans[i] = L[i];
    sort(ans + 1,ans + M + 1);
    M = unique(ans + 1,ans + M + 1) - ans - 1;
    R = MOD;
    return true;
    }
    int tot = 0;
    for(int i = 1 ; i <= M ; ++i) {
    for(int j = 1 ; j <= cntL ; ++j) {
        tmp[++tot] = (R * Inv(R,MOD) % (R * MOD) * (L[j] - ans[i]) + ans[i]) % (R * MOD);
        tmp[tot] = (tmp[tot] + R * MOD) % (R * MOD);  
    }
    }
    R *= MOD;
    sort(tmp + 1,tmp + tot + 1);
    tot = unique(tmp + 1,tmp + tot + 1) - tmp - 1;
    for(int i = 1 ; i <= tot ; ++i) ans[i] = tmp[i];
    M = tot;
    return true;
}
void Solve() {
    M = 0;
    if(B % 2 == 0) {
    int64 Now = 2;B /= 2;
    if(C & 1) ans[++M] = 1;
    else ans[++M] = 0;
    
    while(B % 2 == 0) {
        B /= 2;
        Now *= 2;
        int t = 0;
        for(int i = 1 ; i <= M ;++i) {
        if(fpow(ans[i],A,Now) == C % Now) tmp[++t] = ans[i];
        if(fpow(ans[i] + Now / 2,A,Now) == C % Now) tmp[++t] = ans[i] + Now / 2;
        }
        for(int i = 1 ; i <= t ; ++i) ans[i] = tmp[i];
        if(!t) goto fail;
        M = t;
    }
    R = Now;
    sort(ans + 1,ans + M + 1);
    M = unique(ans + 1,ans + M + 1) - ans - 1;
    }
    for(int64 i = 3 ; i <= B / i ; ++i) {
    if(B % i == 0) {
        eu = (i - 1);
        B /= i;
        int num = i,cnt = 1;
        while(B % i == 0) {
        B /= i;eu *= i;num *= i;++cnt;
        }
        G = primitive_root(num,eu);
        if(!Process(A,C,i,cnt)) goto fail;
    }
    }
    if(B > 1) {
    eu = B - 1;
    G = primitive_root(B,eu);
    if(!Process(A,C,B,1)) goto fail;
    }
    if(M == 0) goto fail;
    sort(ans + 1,ans + M + 1);
    for(int i = 1 ; i <= M ; ++i) {
    printf("%d%c",ans[i]," \n"[i == M]);
    }
    return;
    fail:
    puts("No Solution");
}
int main() {
#ifdef ivorysi
    freopen("f1.in","r",stdin);
#endif
    int T;
    scanf("%d",&T);
    while(T--) {
    scanf("%lld%lld%lld",&A,&B,&C);
    Solve();
    }
}

以上是关于51nod1123 X^A Mod B (任意模数的K次剩余)的主要内容,如果未能解决你的问题,请参考以下文章

51nod 1038X^A Mod P

解高次同余方程51nod1038 X^A Mod P

51nod 1172 Partial Sums V2 卡精度的任意模数FFT

51NOD-01046 A^B Mod C

51Nod 1046 A^B Mod C(日常复习快速幂)

计算幂 51Nod 1046 A^B Mod C