数学填坑专辑

裴蜀定理

我的这篇博客

拓展欧几里得定理

当已知\(a\)\(b\)时,求一组\(x\)\(y\)使得\(a\times x + b\times y = GCD(a, b)\)

  • 解一定存在

  • 因为\(GCD(a,b) = GCD(b, a \% b)\),所以\(a \times x + b \times y = GCD(b, a \%b ) = b \times x + (a \% b) \times y = b \times x + (a - \lfloor \frac ab \rfloor \times b) \times y = a \times y + b \times (x - \lfloor \frac ab \rfloor \times y)\)

    所以,我们成功地由\(a\)\(b\)的线性组合转化成了\(b\)\(a \%b\)的线性组合。说人话,我们可以在求\(GCD(a,b)\)的过程中求出一组\(x\)\(y\)了。

应用:

  • 求解线性同余方程

    定理1:对于方程\(a \times x + b \times y = c\)(等价于\(a \times x \equiv c\pmod b\))有整数解的充要条件为,\(GCD(a,b)\mid c\)。(裴蜀定理)

    根据定理一,我们可以用拓展欧几里得定理求出\(a \times x + b \times y = GCD(a,b)\)的一组解,再\(\div GCD(a,b)\)\(\times c\)就行了。

  • 求上面那个方程的最小解

    定理2:\(x_i = x_0 + \frac{b}{GCD(a,b)} \times t\)。其中,\(x_i\)为不定方程的任意解,\(x_0\)为已知解,\(t\)为任意正整数。

    由定理2可知,\(x_{min} = x_0 + \frac{b}{GCD(a,b)} \times t\),所以\(x_{min} = x_0 \mod \frac{b}{GCD(a,b)}\)。别忘了特判\(x \leq 0\)的情况,若\(x \leq 0\)\(x_{min} = x_0 \mod \frac{b}{GCD(a,b)} + \frac{b}{GCD(a,b)}\)

    (其实上面那个式子是看花姐的,本人太弱了,并不会证……

    一道模板题

整数分解

模板题

题意:给出两个整数\(a\)\(b\),求\(a^b\)的因子和。

前置芝士:整数的唯一分解定理,约数和公式,等比数列求和,快速幂

  1. 整数的唯一分解定理

    任意正整数都有且只有一种方式写出其素因子的乘积表达式:

    \[A = (p_1 ^ {k_1}) \times (p_{2} ^ {k_2}) \times (p_3 ^{k_3} * ) \times \cdot\cdot\cdot \times(p_n^{k_n}) \]

    其中,\(p_i\)为素数。

  2. 约数和公式

    对于已经分解的整数\(A = (p_1 ^ {k_1}) \times (p_{2} ^ {k_2}) \times (p_3 ^{k_3} * ) \times \cdot\cdot\cdot \times(p_n^{k_n})\)

    \(A\)的所有因子之和为:

    \[S = (1+p_1+p_1^2+p_1^3+\cdot\cdot\cdot+p_1^{k_1})\times(1+p_2+p_2^2+p_2^3+\cdot\cdot\cdot+p_2^{k_2})\times\cdot\cdot\cdot\times(1+p_n+p_n^2+p_n^3+\cdot\cdot\cdot+p_n^{k_n}) \]

  3. 等比数列求和(用递归二分求\(1+p+p^2+p^3+\cdot\cdot\cdot+p^n\))

    • \(n\)\(0\),则答案为\(1\)

    • \(n\)为奇数,一共有偶数项,则

      \[1+p+p^1+p^2+p^3+\cdot\cdot\cdot+p^n\\=(1+p^{n/2+1})+p\times(1+p^{n/2+1})+\cdot\cdot\cdot+p^{n/2}\times(1+p^{n/2+1})\\=(1+p+p^2+\cdot\cdot\cdot+p^{n/2})\times(1+p^{n/2+1}). \]

    • \(n\)为偶数,就可以把\(n\)看做\(n-1\),按照奇数做法,将最后答案\(+p^n\)就可以了

  4. 快速幂(过于基础,不再赘述)

然后我们就可以愉快地做这道题了:)

参考代码

#include <cstdio>
#define LL long long

using namespace std;

const int mode = 9901, maxn = 1e4 + 10;;

LL n[maxn],p[maxn],cnt,a,b;

LL q_pow(LL x, int y){
    LL ans = 1;
    while(y){
        if(y & 1) ans *= x, ans %= mode;
        x *= x, x %= mode;
        y >>= 1;
    } return ans;
}

int get_sum(LL p, LL n){
    if(n == 0) return 1;
    LL ans = 0;
    if(!(n & 1)) ans = q_pow(p, n), n -= 1;
    return (ans + (get_sum(p, n / 2) * (1 + q_pow(p, n / 2 + 1)) % mode) % mode ) % mode;
}

int main(){
    scanf("%lld%lld", &a, &b);
    for(int i = 2; i * i <= a;){  //根号法+递归法
        if(a % i == 0){
            p[++cnt] = i;
            n[cnt] = 0;
            while(a % i == 0) n[cnt]++, a /= i;
        }
        if(i == 2) i += 1; //奇偶法
        else i += 2;
    }
    if(a != 1) p[++cnt] = a, n[cnt] = 1;
    LL Ans = 1;
    for(int i = 1; i <= cnt; ++ i)
        Ans = (Ans * get_sum(p[i], n[i] * b)) % mode;  // 别忘了将次数*b
    printf("%lld\n", Ans);
    return 0;
}

做法源于数学一本通(不然我也想不出这么奇怪的命名方式……)

如何\(n\log n\)分解\(n!\):
先筛出\([1,n]\)中每一个质数\(p\),然后考虑\(n!\)中一共有多少个质因子\(p\)\(n!\)中质因子\(p\)的个数等于\([1,n]\)中每一个数质因子中\(p\)的个数之和。在\([1,n]\)中,至少包含一个质因子\(p\)的有\(n/p\)个,至少包含两个质因子\(p\)的有\(n/p^2\)个,但是其中的一个因子在包含一个的时候算过了,所以只需要另外统计一个,即累加上\(n/p^2\)。总复杂度\(O(n\log n)\)

组合数计算

  • 加法递推:\(C(n, m) = C(n - 1, m) + C(n - 1, m - 1)\)

    复杂度:\(O(n^2)\)

  • 乘法递推:\(C(n,m)=C(n,m-1)*(n-m+1)/m\)

    复杂度:\(O(n)\)(注意要先乘后加!

  • 当模数不大时,可以使用卢卡斯定理来降低复杂度

    代码:

    int Lucas(int n, int m){
        if(!m) return 1;
        return (C(n % P, m % P) + Lucas(n / P, m / P)) % P;
    }
    

以上算法都很容易爆long long,必要时请使用高精度运算。

第二类斯特林数

  • 定义:第二类斯特林数\(S(n,m)\)表示的是把\(n\)个不同的小球放在\(m\)个相同箱子里的方案数(箱子不允许为空)。

  • 递推式:\(S(n,m)=S(n-1,m)\times m + S(n-1,m-1)\),边界为:\(S(0,0)=1\)

    证明:

    当新加入一个元素时:

    • 若将新元素单独放入一个子集,则有\(S(n-1,m-1)\)种方案;
    • 若将新元素放入一个现有的非空子集,则有\(S(n-1,m)\)种方案。

    根据加法原理,即可得到递推式。

  • 一些思考:

    • 假如箱子允许为空怎么办?
    • 假如是\(m\)个不同的箱子怎么办?

    Ans1:考虑剩余箱子个数可能为\([1,m]\),所以答案为\(\sum_{i=1}^{m}{S(n,i)}\)

    Ans2:考虑到按照第二类斯特林数的方式排列后,\(m\)个箱子的顺序还会对答案造成影响,因此还需要乘上\(m\)个箱子的排列,即\(m!\times S (n,m)\)

整除分块

  • 主要应用场景:在\(O(\sqrt n)\)时间内求出形如\(\sum_{i=1}^{n}{\lfloor \frac{n}{i} \rfloor}\)

  • 大致思路:

    整除分块基于这样一个事实:\(\lfloor \frac{n}{i} \rfloor\)的取值总共不会超过\(2\sqrt n\)种。

    粗略证明:

    • \(i \le \sqrt n\)时:由于\(i\)的取值少于\(\sqrt n\)种,所以\(\lfloor \frac{n}{i} \rfloor\)的取值也必然少于\(\sqrt n\)种;
    • \(i > \sqrt n\)时:\(1 \le \lfloor \frac{n}{i} \rfloor \le \sqrt n\),所以\(\lfloor \frac{n}{i} \rfloor\)的取值不会超过\(\sqrt n\)种。

    \(\lfloor \frac{n}{i} \rfloor\)不同取值不会超过\(2\sqrt n\)种。

    所以,我们可以考虑这样的思路:因为\(\lfloor \frac{n}{i} \rfloor\)的取值是\(\sqrt n\)级别的,所以是需要求出它取每一种值时\(i\)的最大最小值时多少,就可以很快求出答案了。

    接下来又需要另外一个结论:如果已知正整数\(p\)\(n\)\(n \ge p\)),则使\(\lfloor \frac{n}{i} \rfloor = \lfloor \frac{n}{p} \rfloor\)的最大整数\(i\)的值\(i_{max} = \frac{n}{\lfloor \frac{n}{p} \rfloor}\)。(比较好理解,这里就不证明了)

    于是我们只要枚举左端点就好了。

  • 模板题:

    [CQOI2007]余数求和

    Luogu3935 Calculating

    第二题参考代码:

    #include <cstdio>
    #include <algorithm>
    #define LL long long
    
    using namespace std;
    
    const int P = 998244353;
    LL l,r,sum1,sum2;
    
    int main(){
        scanf("%lld%lld", &l, &r); l -= 1;
        for(LL i = 1; i <= l;){
            LL j = min(l / (l / i), l);
            sum1 += (j - i + 1) * (l / i) % P; sum1 %= P;
            i = j + 1;
        }
        for(LL i = 1; i <= r;){
            LL j = min(r / (r / i), r);
            sum2 += (j - i + 1) * (r / i) % P; sum1 %= P;
            i = j + 1;
        }
        printf("%lld\n", (sum2 - sum1 + P) % P);
        return 0;
    }
    

更相损减术

  • 可半者半之,不可半者,副置分母、子之数,以少减多,更相减损,求其等也。以等数约之。

    翻译:(如果需要对分数进行约分,那么)可以折半的话,就折半(也就是用2来约分)。如果不可以折半的话,那么就比较分母和分子的大小,用大数减去小数,互相减来减去,一直到减数与差相等为止,用这个相等的数字来约分。

  • 使用步骤:

    第一步:任意给定两个正整数;判断它们是否都是偶数。若是,则用\(2\)约简;若不是则执行第二步。

    第二步:以较大的数减较小的数,接着把所得的差与较小的数比较,并以大数减小数。继续这个操作,直到所得的减数和差相等为止。

    则第一步中约掉的若干个\(2\)的积与第二步中等数的乘积就是所求的最大公约数。

    其中所说的“等数”,就是公约数。求“等数”的办法是“更相减损”法。

    注意:辗转相减的复杂度为\(O(n)\),所以不常用于计算$$gcd$$。

  • 重要推论:\(gcd(a,b)=gcd(a,a-b)(a>b)\)

调和级数

  • \(\sum_{i=1}^{n}{\frac{1}{i}=\ln n+\gamma+\epsilon_n}\),其中\(\gamma\)为欧拉常熟,约为\(0.5772156649\),而\(\epsilon_n\)约等于\(\frac{1}{2n}\),并且随着\(n\)的趋近于正无穷而趋近于\(0\)

  • 应用:

    • 某些题目中用于计算时间复杂度(如CSP2020 T2);
    • 计算刚才的式子(废话)。
  • 模板题:Luogu6028 算数

  • 参考代码:

    #include <cstdio>
    #include <cmath>
    #define db long double
    #define LL long long
    #define beta 0.577216
    
    using namespace std;
    
    const int maxn = 1e7 + 10;
    const int N = 10000000;
    db s[maxn],Ans;
    LL n;
    
    db S(LL loc){
        if(loc <= N) return s[loc];
        else return beta + log(loc) + 1.0 / (2 * loc);
    }
    
    db getsum(LL l, LL r){return S(r) - S(l - 1);}
    
    int main(){
        scanf("%lld", &n);
        for(int i = 1; i <= N; ++ i)
            s[i] = s[i - 1] + 1.0 / i;
        for(LL i = 1; i <= n;){
            LL j = n / (n / i);
            LL val = n / i;
            Ans += (db)val * getsum(i, j);
            i = j + 1;
        }
        printf("%.10Lf\n", Ans);
        return 0;
    }
    

欧拉函数

  • 欧拉函数\(\varphi(n)\)表示\([1,n]\)中与\(n\)互质的数的个数。

  • 一些性质:

    • 欧拉函数是积性函数。即如果\(gcd(a,b)=1\),则\(\varphi(a\times b)=\varphi(a)\times \varphi(b)\)。特别地,当\(n\)是奇数时\(\varphi(2n)=\varphi(n)\)
    • \(n=\sum_{d\|n}{\varphi(d)}\)
    • \(\varphi(p^k)=p^k-p^{k-1}\),其中\(p\)为质数。
    • \(n=\prod_{i=1}^{s}{p_i^{k_i}}\),其中\(p_i\)为质数,那么\(\varphi(n)=n\times \prod_{i=1}^{s}{\frac{p_i-1}{p_i}}\)
    • \(n(n>1)\)中与\(n\)互质的数的和为\(\frac{\varphi(n)\times n}{2}\)。(由更相损减术推论可以推出)

    以上性质的[证明](欧拉函数 - OI Wiki (oi-wiki.org))。

  • 如何求欧拉函数值:

    • 单个数求值:根据最后一个性质分解质因数就行了。复杂度\(O(\sqrt{n})\)

    • 线性筛:

      我们知道,在普通的线性筛中,\(n\)会被最小的质数\(p\)筛去,于是设\(n=n'\times P\)

      分类讨论一下,

      • \(n'\%P=0\),那么\(n'\)包含了\(n\)的所有因子,则:

        \[\begin{align}\varphi(n)&=n\times \prod_{i=1}^{s}{p_i^{k_i}}\\ &=P\times n' \times \prod_{i=1}^{s}{p_i^{k_i}}\\ &=P\times \varphi(n') \end{align} \]

      • \(n'\%P\neq 0\),那么\(n'\)\(P\)互质,则\(\varphi(n)=\varphi(n')\times \varphi(P)\)

      参考代码:

      void init(){
          memset(phi,0,sizeof(phi));
          phi[1] = 1;
          for(int i = 2; i <= n; ++ i){
              if(!vis[i]) p[++cnt] = i, phi[i] = i - 1;
              for(int j = 1; j <= cnt && (LL)i * p[j] <= n; ++ j){
                  vis[i * p[j]] = 1;
                  if(i % p[j] == 0){
                      phi[i * p[j]] = p[j] * phi[i];
                      break;
                  }
                  else phi[i * p[j]] = phi[p[j]] * phi[i];
              }
          }
      }
      
  • 主要用于解决有关互质,GCD的问题,如:

    SDOI2008 仪仗队

    Luogu2568 GCD

    Luogu2398 GCD SUM

欧拉定理

  • \(\gcd(a,m)=1\),则\(a^{\varphi(m)} \equiv 1\pmod m\)。(证明主要是自己不会证……)

  • 拓展欧拉定理:

    \[a^b\equiv \begin{cases}a^{b\:mod\:\varphi(p)},&{gcd(a,p)=1}\\ a^b,&gcd(a,p)\neq1,b<\varphi(p)\\ a^{b\:mod\:\varphi(p)+\varphi(p)}, &gcd(a,p)\neq 1,b\geq\varphi(p) \end{cases}\pmod p \]

    证明地址同上。

  • 应用:Luogu4139 上帝与集合的正确用法

第一类斯特林数

  • 也叫斯特林轮换数,\(S(n,k)\)表示将\(n\)个两两不同的元素,划分成\(k\)个非空园排列的方案数。

  • 递推式:\(S(n,k)=S(n-1,k-1)+(n-1)\times S(n-1,k)\)

    证明:

    与第一类斯特林数类似,考虑新加入一个元素:

    • 将该元素放入一个单独的园排列中,共有\(S(n-1,k-1)\)种方案;
    • 将该元素插入任意一个已有的园排列中,共有\((n-1)\times S(n-1,k)\)种方案(因为有\(n-1\)个位置可选)。
  • 练习题:Hdu3625 Examining the Rooms

中国剩余定理(CRT)

  • 用于求解线性同余方程组,如:

    \[\begin{cases} x &\equiv a_1 \pmod {n_1} \\ x &\equiv a_2 \pmod {n_2} \\ &\vdots \\ x &\equiv a_k \pmod {n_k} \\ \end{cases} \]

  • 一般步骤:

    1. 计算所有模数的积\(n\)

    2. 对于第\(i\)个方程:

      a. 计算\(m_i=\frac{n}{n_i}\)

      b. 计算\(m_i\)在模\(n_i\)意义下的逆元\(m^{-1}\)

      c. 计算\(c_i=m_i\times m_i^{-1}\)不要对\(n_i\)取模!)。

    3. 方程组的唯一解为:\(a=\sum_{i=1}^{k}{a_i\times c_i\pmod n}\)

    参考代码:

    N = 1;
    for(int i = 1; i <= n; ++ i) N *= n[i];
    for(int i = 1; i <= n; ++ i){
        int m = N / n[i];
        int _m,y; ex_gcd(m, n[i], _m, y);
        if(_m < 0) _m += n[i];
        Ans += a[i] * _m * m % N; Ans %= N;
    }
    
  • 证明:暂无(逃

  • 应用:

    1. 在某些题中,处于某些原因,给出的模数不是质数,但是对其质因数分解以后发现它没有平方因子,那么我们可以对这些质数的质因子进行计算,最后用\(CRT\)整合答案。

      例如:SDOI2010古代猪文

      • \(g=999911659\)时,答案为\(0\)

      • 否则,根据欧拉定理,

        \[g^{\sum_{d\|n}{\C^{d}_{n}}}\mod999911659=g^{\sum_{d\|n}{\C^{d}_{n}} \mod 999911658}\mod 999911659 \]

        所以就是要想办法求出\(\sum_{d\|n}{\C^{d}_{n}}\mod 999911658\)。但是\(999911658\)不是质数,无法直接计算,但是注意到\(999911658=2\times 3\times 4679\times 35617\),没有平方因子,所以我们可以分别求出\(\sum_{d\|n}{\C^{d}_{n}}\)在模\(2,3,4679,35617\)时的值(使用卢卡斯定理),再用中国剩余定理整合答案。

        也就是说,要求一个这样的方程组的解:

        \[\begin{cases}x &\equiv a_1 \pmod {2} \\x &\equiv a_2 \pmod {3} \\ x &\equiv a_3 \pmod {4679} \\x &\equiv a_4 \pmod {35617} \\\end{cases} \]

        参考代码:

        #include <cstdio>
        #include <cstring>
        #define int long long
        
        using namespace std;
        
        const int Max = 35617;
        int Num[Max + 10];
        int n,a[20],b[20],N,Ans,num,g;
        int P;
        
        void init(){
            memset(Num,0,sizeof(Num));
            Num[1] = Num[0] = 1;
            for(int i = 2; i <= P; ++ i) Num[i] = Num[i - 1] * i % P;
        }
        
        int Pow(int x, int y){
            int ans = 1;
            while(y){
                if(y & 1) ans *= x, ans %= P;
                x *= x, x %= P;
                y >>= 1;
            } return ans;
        }
        
        void ex_gcd(int a, int b, int &x, int &y){
            if(!b){
                x = 1, y = 0;
                return;
            }
            ex_gcd(b, a % b, x, y);
            int z = x; x = y; y = z - (a / b) * y;
        }
        
        int C(int n, int m){
            if(n < m) return 0;
            return Num[n] * Pow(Num[n - m], P - 2) % P * Pow(Num[m], P - 2) % P;
        }
        
        int Lucas(int n, int m){
            if(!m) return 1;
            return (C(n % P, m % P) * Lucas(n / P, m / P)) % P;
        }
        
        int solve(int n){
            Ans = 0; int i;
            for(i = 1; i * i < n; ++ i)
                if(n % i == 0){
                    Ans += (Lucas(n, i) + Lucas(n, n / i)) % P;
                    Ans %= P;
                }
            if(i * i == n) Ans += Lucas(n, i), Ans %= P;
            return Ans;
        }
        
        int CRT(){
            N = 1; Ans = 0;
            for(int i = 1; i <= n; ++ i) N *= a[i];
            for(int i = 1; i <= n; ++ i){
                int m = N / a[i];
                int _m,y; ex_gcd(m, a[i], _m, y);
                if(_m < 0) _m += a[i];
                Ans += b[i] * _m * m % N; Ans %= N;
            }
            return Ans;
        }
        
        signed main(){
            scanf("%lld%lld", &num, &g);
            if(g == 999911659) return printf("0\n"), 0;
            n = 4;
            a[1] = 2, a[2] = 3, a[3] = 4679, a[4] = 35617;
            for(int i = 1; i <= 4; ++ i){ 
                P = a[i]; init();
                b[i] = solve(num);
            }
            P = 999911659;
            printf("%lld\n", Pow(g, CRT()));
            return 0;
        }
        

BSGS(大步小步法)

  • 用于\(O(\sqrt P)\)时间内求解\(a^x \equiv b \pmod P\)

    其中\(a\)\(P\)互质。方程的解满足\(0\le x< P\)。注意:只需要互质,不要求\(P\)为质数。

  • 算法描述:

    \(x=A\lceil \sqrt P \rceil - B\),其中\(0\le A,B \le \lceil \sqrt P \rceil\),则有\(a^{A\left \lceil \sqrt P \right \rceil -B} \equiv b \pmod P\),进而得到\(a^{A\left \lceil \sqrt P \right \rceil} \equiv ba^B \pmod P\)

    我们已知\(a,b\),所以可以先算出右边的\(ba^B\)的所有取值,枚举\(B\),用\(hash/map\)存储,然后计算\(a^{A\left \lceil \sqrt P \right \rceil}\),枚举\(A\),寻找是否有与之相等的\(ba^B\)。这样就可以得到所有的\(x\)了。

    因为\(A,B\)均小于\(\lceil \sqrt P \rceil\),所以复杂度为\(O(\sqrt P)\),如果用\(map\),则需要加一个\(log\)

  • ex_BSGS(拓展大步小步法)

    用于处理\(a\)\(P\)不互质的情况。

    既然他们不互质,那么就让他们变得互质。

    \(GCD=gcd(a,P)\)。若\(GCD\nmid b\),则原方程无解,否则我们让两边同时除以\(GCD\),得到

    \[\frac{a}{GCD}\cdot a^{x-1}\equiv \frac{b}{GCD}\pmod{\frac{P}{GCD}} \]

    如果\(a\)\(\frac{P}{GCD}\)仍不互质,那么将\(\frac{P}{GCD}\)作为\(P\)再次进行上一步。

    \(D=\prod_{i=1}^k{GCD_i}\),那么:

    \[\frac{a}{D}\cdot a^{x-k}\equiv \frac{b}{D}\pmod{\frac{P}{D}} \]

    其中\(a\)\(\frac{P}{D}\)互质,所以可以使用\(BSGS\)求出一个\(x\),最后只需要将\(x+k\)即可。

    注意:在实现过程中,有可能出现\(\frac{a}{D}=\frac{P}{D}\)的情况,这时候\(a^{x-k}\equiv 1\pmod{\frac{P}{D}}\),所以\(x-k=0\),将\(k\)作为答案返回即可。

    代码实现:

    //Luogu P4195 【模板】拓展BSGS
    //c++11下测评
    #include <cstdio>
    #include <map>
    #include <unordered_map>
    #include <cmath>
    #include <algorithm>
    #define int long long
    
    using namespace std;
    
    int n,m,P;
    unordered_map<int,int> M;
    
    int Pow(int x, int y){
        int ans = 1;
        while(y){
            if(y & 1) ans *= x, ans %= P;
            x *= x, x %= P;
            y >>= 1;
        }
        return ans;
    }
    
    int ex_gcd(int a, int b, int &x, int &y){
        if(!b){x = 1, y = 0; return a;}
        int ret = ex_gcd(b, a % b, x, y);
        int z = x; x = y, y = z - (a / b) * y;
        return ret;
    }
    
    int gcd(int a, int b){return (!b) ? a : gcd(b, a % b);}
    
    int BSGS(int a, int b, int Num){
        M.clear();
        int sq = ceil(sqrt(P));
        int ret = Pow(a, sq), num = 1;
        int x,y; ex_gcd(Num, P, x, y);
        x = (x % P + P) % P;
        b *= x, b %= P;
        M[b] = 0;
        for(int i = 1; i <= sq; ++ i){
            num *= a; num %= P;
            M[num * b % P] = i;
        }
        num = 1;
        for(int i = 1; i <= sq; ++ i){
            num *= ret; num %= P;
            if(M.find(num) != M.end()){
                int X = i * sq - M[num]; X %= P;
                return (X + P) % P;
            }
        }
        return -1;
    }
    
    int ex_BSGS(int a, int b){
        if(!b) return 0;
        int k = 0, num = 1, GCD = 0;
        while((GCD = gcd(a, P)) > 1){
            if(b % GCD) return -1;
            k ++, b /= GCD, P /= GCD, num = num * (a / GCD) % P;
            if(num == b) return k;
        }
        num = BSGS(a, b, num);
        if(num != -1) num += k;
        return num;
    }
    
    signed main(){
        while(scanf("%lld%lld%lld", &n, &P, &m) != EOF && (n || P || m)){
            int ret = ex_BSGS(n, m);
            if(ret == -1) printf("No Solution\n");
            else printf("%lld\n", ret);
        }
        return 0;
    }
    

持续更新……

posted @ 2020-11-14 15:53  When_C  阅读(361)  评论(0编辑  收藏  举报