逆元(Inverse element)就是在mod意义下,不能直接除以一个数,而要乘以它的逆元。
比如 a ∗ b ≡ 1 ( m o d p ) a*b≡1(mod p) a∗b≡1(modp), a , b a,b a,b互为模 p p p意义下的逆元,就是当你想要求 x / a x/a x/a就可以改为求 x ∗ b % p x*b\%p x∗b%p
观察 a ∗ b ≡ 1 ( m o d p ) a*b≡1(mod p) a∗b≡1(modp),可以变形为 a ∗ b + k ∗ p = 1 a*b+k*p=1 a∗b+k∗p=1,就可以用扩展欧几里得算法求 a a a了,同时这里也说明了 a a a和 p p p只有在互素的情况下才存在逆元。
在计算 a / b a/b a/b时,算出 b b b关于 m o d mod mod的逆元后,最好再对 a a a求一下 a % m o d a\%mod a%mod后再相乘 a ∗ i n v ( b ) % m o d a*inv(b)\%mod a∗inv(b)%mod,防止爆 l o n g l o n g long long longlong。
#include
#define ll long long
using namespace std;
const int N = 1e5 + 10;
ll a, mod = 1000000007, t;
ll ppow(ll a, ll b) {
ll ans = 1;
while(b > 0) {
if(b & 1)
ans = ans * a % mod;
a = a * a % mod;
b >>= 1;
}
return ans;
}
ll exgcd(ll a, ll b, ll &x, ll &y) {
if(b == 0) {
x = 1, y = 0;
return a;
}
ll t = exgcd(b, a % b, y, x);
y -= a / b * x;
return t;
}
int main() {
scanf("%lld", &t);
while(t--) {
scanf("%lld", &a);
ll an, y;
ll ni = ppow(2, a - 1);//求a关于mod的逆元
ll gcd = exgcd(ni, mod, an, y);
an = (an % mod + mod) % mod;//an就是求出来的逆元
printf("%lld\n", (an * (a % mod)) % mod);//a%mod防止爆ll
}
return 0;
}
性能分析:
费马小定理:若 p p p为素数,则有 a p − 1 ≡ 1 ( m o d p ) a^{p-1}≡1(modp) ap−1≡1(modp)
a p − 2 ∗ a ≡ 1 ( m o d p ) a^{p-2}*a≡1(modp) ap−2∗a≡1(modp)
则 a p − 2 a^{p-2} ap−2就是 a a a在模 p p p意义下的逆元了。
欧拉定理:若a、p互素,则有 a φ ( p ) ≡ 1 ( m o d p ) a^{φ(p)}≡1(modp) aφ(p)≡1(modp)(费马小定理的一般形式)
a φ ( p ) ∗ a ≡ 1 ( m o d p ) a^{φ(p)}*a≡1(modp) aφ(p)∗a≡1(modp)
则 a φ ( p ) − 1 a^{φ(p)-1} aφ(p)−1就是 a a a在模 p p p意义下的逆元了。
ll qkpow(ll a, ll p, ll mod) {
ll t = 1, tt = a % mod;
while(p) {
if(p & 1)
t = t * tt % mod;
tt = tt * tt % mod;
p >>= 1;
}
return t;
}
ll getInv(ll a, ll mod) {
return qkpow(a, mod - 2, mod);
}
性能分析:
ll inv[mod + 5];
void getInv(ll mod) {
inv[1] = 1;
for(int i = 2; i < mod; i++)
inv[i] = (mod - mod / i) * inv[mod % i] % mod;
}
注意:
性能分析: