参考文献
扩展欧几里得+求单个逆元
typedef long long ll;
ll exgcd(ll a,ll b,ll &x,ll &y){//扩展欧几里得算法 ax+by=c 求解a,b
if(!b){
x=1,y=0;
return a;
}
ll ans=exgcd(b,a%b,x,y);
ll temp=x;
x=y;
y=temp-a/b*y;
return ans;
}
ll inv(ll a,ll p){//求逆元 ax1(mod p)=>a*x+p*y=1 这里我们求x 要求a与p互质,即gcd(a,p)=1 如果不存在返回-1
ll x,y,gcd;
gcd=exgcd(a,p,x,y);
return (x%p+p)%p;; //x%p+p确保逆元为正数
}
逆元打表
一、 1 ~ n 的逆元
递推公式:
inv[i] = ( p - p / i ) * inv[p % i] % p
证明过程:
int inv[N]={0,1};
void invp(int p) {//1 ~ p-1(这里是1~N,那么一定要保证N<p) 模p的逆元 时间复杂度 O(N) inv[i] = (p-p/i) * inv[p%i] % p
for (i = 2; i <= N; i++) {
inv[i] = ((p - p/i) * inv[p%i] ) % p;
}
}
二、n! 的逆元
递推公式:
inv[i] = inv[i + 1] * (i + 1) % MOD
证明过程:
应用:求组合数
原理:
C(n,m) = [ n! / (m! * (n-m)! ) ] % mod
= n! % mod * ([m!]^-1 ) * ( [(n-m)!]^-1) % mod
//求1!~n!的逆元打表 一般用于求组合数
#include<cstdio>
const int N = 2e6+5;
const int mod = 20100403;
int jc[N]; //阶乘表
int inv[N]; // N! 的逆元!! 注意 下标是i,实际含义是i!
int n,m;
int power(int a,int n){//快速幂
int ans = 1;
while(n){
if(n&1) ans = 1ll * ans * a % mod;
a = 1ll * a * a % mod;
n >>= 1;
}
return ans;
}
void jiecheng(){//阶乘取模打表
jc[0] = 1;
for(int i = 1;i < N;++i) jc[i] = 1ll * jc[i-1] * i % mod;
}
void invt(){//逆元打表
inv[N-1] = power(jc[N-1],mod-2);
for(int i = N-2;i >= 0;--i) inv[i] = (1ll * inv[i+1] * (i+1)) % mod;
}
int C(int n,int m){//求组合数,n为下标,m为上标
int z = 1ll * jc[n] * inv[m] % mod;
return 1ll * z * inv[n-m] % mod;
}
int main(){
scanf("%d%d",&n,&m);
// cout<<C(n+m,n)<<endl;
// cout<<C(n+m,n-1)<<endl;
// 卡特兰数
printf("%d\n",(C(n+m,m) - C(n+m,m-1) + mod) % mod);
return 0;
}