问题引入
计算 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} a≥1018,b≥1018,快速幂显然会溢出
- 换一个范围,如果现在 b ≥ 1 0 200000000000 b\geq 10^{200000000000} b≥10200000000000,一个极大的数,那我们势必要先对 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)) ab≡abmodφ(n)+φ(n)(modn)(b≥φ(n))如果 b < φ ( n ) b\lt \varphi(n) b<φ(n),那么要直接利用快速幂计算,千万注意使用条件。利用这个公式我们就可以把 b b b逐层降幂,在使用的时候需要注意每一层对应的是哪一个 φ ( n ) \varphi(n) φ(n),这个在后面有对应的例题 - 上述定理证明请自行百度,数学系学生该干的事
例题
- 先敲两道模板
https://www.luogu.com.cn/problem/P5091
https://www.luogu.com.cn/problem/U55950 - 下面这个需要用慢速乘或者快速乘,数很大
- 注意 b b b取完模结果是 0 0 0的情况
#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+1−3
- 得到公式以后,也不能够直接出结果,因为这里指数位置太大,需要欧拉降幂,有一种非常好的写法是从后往前递归,这和前一题很像,需要记录每一层的欧拉函数值是多少,指数对这个数取模
#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;
}