Skip to content

求逆元

相关定理

  1. 费马小定理,有np11(modp)。 因此1nnp2(modp)

  2. 方程ax1(modp)的解被称为a关于模p的逆元。当p为素数且gcd(a,p)=1(此时必有解且唯一)时,由费马小定理可得xap2(modp)

    例如: x=52modp可以转化为x=52p2%p

求逆元方法:

通过扩展欧几里得算法求逆元

cpp
// when we used this method to calculate x / y % p, we also need x and p is coprime 
// Method 1: 	Extended Euclid
// Requires:	a and b is coprime
// Modify:  	x, y
// Effects: 	when the method is finished, x is a's inverse element about b.
// Return:  	gcd(a, b)

int ex_gcd(int a, int b, int &x, int &y) {
	int ret, tmp;
	if (b == 0) {
		x = 1;
		y = 0;
		return a;
	}
	ret = ex_gcd(b, a % b, x, y);
	tmp = x;
	x = y;
	y = tmp - a / b * y;
	return ret;
}

通过递推求1~n的逆元

inv[i]=(ppi)inv[p%i]%p

这个算法可以在O(n)的时间复杂度内求出n以内所有数的逆元,不过需要注意的是p必须为奇质数。

cpp
// when we used this method to calculate x / y % p, we also need x and p is coprime
// Method 3:	Recursive to get i's inverse element
// Requires:	p is prime and p != 2
// Modify:  	inv[]
// Effects: 	inv[i] is i's inverse element about p;

int inv[N];
void get_inverse(int n, int p) {
	inv[1] = 1;
	for (int i = 2; i <= n; ++i) {
		inv[i] = (p - p / i) * inv[p % i] % p;
	}
}

阶乘的逆元:

invf[i]=invf[i+1](i+1)%p
cpp
// when we used this method to calculate x / y % p, we also need x and p is coprime
// Method 4:	Recursive to get Factorial[i] and their inverse element
// Requires:	p is prime
// Modify:  	invf[], factor[]
// Effects:  	factor[i] is i's factorial, invf[i] is factor[i]'s inverse element

int invf[N], factor[N];
void get_factorial_inverse(int n, int p) {
	factor[0] = 1;
	for (int i = 1; i <= n; ++i) {
		factor[i] = i * factor[i - 1] % p;
	}
	invf[n] = quick_inverse(factor[n], p);
	for (int i = n-1; i >= 0; --i) {
		invf[i] = invf[i + 1] * (i + 1) % p;
	}
}

HDU5698 瞬间移动(求逆元)

我们要从(1,1)点走到(n,m)点,只能向右下角的格子里走,那么(1,1)所在的行和列不能走,(n,m)所在的行和列不能走,那么能走的面积就是(n2)(m2)这么大的面积,行和列分别考虑,实质上就是求组合数C(n2)+(m2)n2

因为Cnm=n!m!(nm)!,乘以一个数等于乘这个数的逆元,我们先预处理出阶乘的逆元。

首先求出一个数模p的逆元:

f[i]=(ppi)f[p%i]%p

然后再递推出n!的逆元

inv[(n1)!]=inv[n!]n%p

可以得到

inv[n!]=inv[(n1)!]f[n]%p

然后就可以求出组合数了

代码

cpp
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const ll N=200000+20;
ll fac[N]= {1,1},inv[N]= {1,1},f[N]= {1,1};
/*
fac[i]:i的阶乘
inv[i]:i的阶乘的逆元
f[i]:i的逆元
*/
ll C(ll a,ll b)//除以一个数等于乘这个数的逆元
{
    return fac[a]*inv[b]%mod*inv[a-b]%mod;
}
void init()
{
    for(ll i=2; i<N; i++)
    {
        fac[i]=fac[i-1]*i%mod;
        f[i]=(mod-mod/i)*f[mod%i]%mod;
        inv[i]=inv[i-1]*f[i]%mod;
    }
}

int main()
{
    ll n,m;
    init();
    while(cin>>n>>m)
        cout<<C(m+n-4,m-2)<<endl;
    return 0;
}

基于 MIT 协议发布 · 使用 VitePress 构建