扩展欧拉定理(欧拉降幂)简单整理

问题引入

计算 a b m o d    p a^b\mod p abmodp,其中 a , b , p a,b,p a,b,p均为正整数

  • 看一下这个问题,也许你想使用快速幂来计算,但是如果现在 a ≥ 1 0 18 , b ≥ 1 0 18 a\geq 10^{18},b\geq 10^{18} a1018,b1018,快速幂显然会溢出
  • 换一个范围,如果现在 b ≥ 1 0 200000000000 b\geq 10^{200000000000} b10200000000000,一个极大的数,那我们势必要先对 b b b取模,但是现在 b b b是处于指数的位置,直接取模肯定是错的,所以我们引入欧拉降幂的方法

欧拉降幂

  • 首先确保已经会了欧拉函数,接下来直接给出欧拉定理和扩展欧拉定理

欧拉定理

  • a , n a,n a,n均为正整数,且 a , n a,n a,n互质, φ ( n ) \varphi(n) φ(n)表示 n n n的欧拉函数,则 a φ ( n ) ≡ 1 ( m o d n ) a^{\varphi(n)}\equiv1\pmod n aφ(n)1(modn)费马小定理是欧拉定理的一个特殊情况,因为它规定了 n n n必须是质数
  • 推论 a b = a ⌊ b φ ( n ) ⌋ × φ ( n ) + b m o d    φ ( n ) ≡ 1 ×   a b m o d    φ ( n ) ( m o d n ) a^{b}=a^{\lfloor\frac{b}{\varphi(n)}\rfloor\times\varphi(n)+b \mod \varphi(n)}\equiv1\times\ a^{b\mod \varphi(n)}\pmod n ab=aφ(n)b×φ(n)+bmodφ(n)1× abmodφ(n)(modn)根据上式,我们就可以计算出 b b b比较大的情况了,但是注意 a , n a,n a,n需要互质

扩展欧拉定理

  • 那么如果 a , n a,n a,n不能保证互质,怎么办呢?直接给出扩展欧拉定理公式如下
    a b ≡ a b m o d    φ ( n ) + φ ( n ) ( m o d n ) ( b ≥ φ ( n ) ) a^{b}\equiv a^{b\mod \varphi(n)+\varphi(n)}\pmod n(b\geq\varphi(n)) ababmodφ(n)+φ(n)(modn)(bφ(n))如果 b < φ ( n ) b\lt \varphi(n) b<φ(n),那么要直接利用快速幂计算,千万注意使用条件。利用这个公式我们就可以把 b b b逐层降幂,在使用的时候需要注意每一层对应的是哪一个 φ ( n ) \varphi(n) φ(n),这个在后面有对应的例题
  • 上述定理证明请自行百度,数学系学生该干的事

例题

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
ll slowmul(ll x, ll y, ll mod){
    
    
    ll ans = 0;
    while(y){
    
    
        if(y & 1){
    
    
            ans = (ans + x);
            if(ans > mod) ans %= mod;
        }
        x = 2 * x % mod;
        y >>= 1;
    }
    return ans;
}
ll fastpow(ll base, ll power, ll mod){
    
    
    ll ans = 1;
    while(power){
    
    
        if(power & 1) ans = slowmul(ans, base, mod);
        base = slowmul(base, base, mod);
        power >>= 1;
    }
    return ans;
}
ll euler(ll n){
    
    
    ll ans = n;
    for(ll i=2;i*i<=n;i++){
    
    
        if(n % i == 0){
    
    
            ans = ans / i * (i - 1);
            while(n % i == 0){
    
    
                n /= i;
            }
        }
    }
    if(n > 1){
    
    
        ans = ans / n * (n - 1);
    }
    return ans;
}
inline ll read(){
    
    
    ll x = 0, f = 1;char c = getchar();
    while(c < '0' || c > '9'){
    
    if(c == '-') f = -1;c = getchar();}
    while(c >= '0' && c <= '9'){
    
    x = (x << 1) + (x << 3) + (c ^ 48);c=getchar();}
    return x*f;
}
bool flag = false;
inline ll read(ll MOD){
    
    
    ll x = 0, f = 1;char c = getchar();
    while(c < '0' || c > '9'){
    
    if(c == '-') f = -1;c = getchar();}
    while(c >= '0' && c <= '9'){
    
    x = (x << 1) + (x << 3) + (c ^ 48);if(x >= MOD){
    
    flag = true;x%=MOD;} c=getchar();}
    return x*f;
}
int main(){
    
    
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    ll a, m, b;
    a = read();
    m = read();
    ll phi = euler(m);
    b = read(phi);
    // cout << a << ' ' << m << ' ' << b << ' ' << phi << '\n';
    if(flag) b += phi;
    if(b >= phi) cout << fastpow(a, b % phi + phi, m);
    else cout << fastpow(a, b, m);
    return 0;
}

https://www.luogu.com.cn/problem/P4139
计算 2 2 2 2 2 2次方的 2 2 2次方的 . . . . . . ...... ...... p p p

  • 看起来好像很神奇,为什么无穷次方之后模 p p p会是一个定值,实际上在欧拉降幂到一定次数之后欧拉函数就会变成 1 1 1,任何数对 1 1 1取模都是 0 0 0,所以加上若干个 2 2 2以后有一个临界点指数为 0 0 0
  • 这道题需要注意递归每一层对应的是哪一个欧拉函数值,欧拉函数值递归层数不多就降到 1 1 1,所以也不会爆栈,因为 b b b一直是无穷大,所以满足 b ≥ φ ( n ) b\geq \varphi(n) bφ(n),直接使用公式即可
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
ll fastpow(ll base, ll power, ll mod){
    
    
    ll ans = 1;
    while(power){
    
    
        if(power & 1) ans = ans * base % mod;
        base = base * base % mod;
        power >>= 1;
    }
    return ans;
}
ll euler(ll n){
    
    
    ll ans = n;
    for(ll i=2;i*i<=n;i++){
    
    
        if(n % i == 0){
    
    
            ans = ans / i * (i - 1);
            while(n % i == 0){
    
    
                n /= i;
            }
        }
    }
    if(n > 1) ans = ans / n * (n - 1);
    return ans;
}
ll solve(ll x){
    
    
    if(x == 1) return 0;
    ll y = euler(x);
    return fastpow(2, solve(y) + y, x);
}
int main(){
    
    
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t;
    cin >> t;
    ll p;
    while(t--){
    
    
        cin >> p;
        cout << solve(p) << '\n';
    }
    return 0;
}

https://ac.nowcoder.com/acm/contest/20325/A
此题是2018年牛客多校赛的一道题,说有一个字符串由 0 , 1 , 2 0,1,2 0,1,2三种字符组成,其中 1 1 1每秒会变成 10 10 10 2 2 2会变成 21 21 21,但是每秒第一个字符会被删除,问多少秒这个字符串能够变成空字符串

  • 这样考虑,我们从前往后假设到一个位置,经过了 t t t秒,那么如果这个位置是 0 0 0,那么直接花费一秒删掉即可,因为他不会分裂;如果是 1 1 1,那么现在已经经过了 t t t秒,所以这个 1 1 1现在应该多产生了 t t t 0 0 0,在 t + 1 t+1 t+1秒时他又产生一个 0 0 0,但自身消失,这时候一共有 t + 1 t+1 t+1 0 0 0,所以这一位一共需要 t + 2 t+2 t+2秒变空;如果是 2 2 2,找规律最后发现是 3 × 2 t + 1 − 3 3\times 2^{t+1}-3 3×2t+13
  • 得到公式以后,也不能够直接出结果,因为这里指数位置太大,需要欧拉降幂,有一种非常好的写法是从后往前递归,这和前一题很像,需要记录每一层的欧拉函数值是多少,指数对这个数取模
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const ll MOD = 1e9 + 7;
ll fastpow(ll base, ll power, ll mod){
    
    
    ll ans = 1;
    while(power){
    
    
        if(power & 1) ans = ans * base % mod;
        base = base * base % mod;
        power >>= 1;
    }
    return ans;
}
ll euler(ll n){
    
    
    ll ans = n;
    for(ll i=2;i*i<=n;i++){
    
    
        if(n % i == 0){
    
    
            ans = ans / i * (i - 1);
            while(n % i == 0) n /= i;
        }
    }
    if(n > 1) ans = ans / n * (n - 1);
    return ans;
}
int main(){
    
    
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t;
    cin >> t;
    string s;
    map<ll, ll> mp;
    function<void(ll)> Init = [&](ll n){
    
    
        if(n == 1){
    
    
            mp[n] = 1;
            return;
        }
        ll y = euler(n);
        Init(y);
        mp[n] = y;
    };
    Init(MOD);
    while(t--){
    
    
        cin >> s;
        int len = s.length();
        function<ll(int, ll)> Dfs = [&](int p, ll x){
    
    
            if(p == -1) return 0ll;
            else if(s[p] == '0') return (Dfs(p - 1, x) + 1) % x;
            else if(s[p] == '1') return (2 * Dfs(p - 1, x) + 2) % x;
            else return (3 * fastpow(2, Dfs(p - 1, mp[x]) + 1, x) - 3 + x) % x;
        };
        cout << Dfs(s.length() - 1, MOD) << '\n';
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/roadtohacker/article/details/120833369
今日推荐