lucas定理
lucas定理
快速求组合数取模
注:mod是少于1e5的质数
核心代码
//C(n, m) % mod
LL lucas(LL n, LL m, LL mod) {
if(m == 0) return 1LL;
return (C(n % mod, m % mod, mod) * lucas(n / mod, m / mod, mod)) % mod;
}
//关于C(n % mod, m % mod) % mod,可以这样求
//第一种
LL C(LL n, LL m, LL mod) {
LL inv[maxn];
inv[1] = 1;
LL f = 1;
for(int i = n - m + 1; i <= n; i++) f = f * i % mod;//(n-m+1)*(n-m+2)*...*n
for(int i = 2; i <= m; i++) inv[i] = (mod - mod / i) * 1LL * inv[mod % i] % mod;//打表1~m关于mod的逆元
for(int i = 2; i <= m; i++) inv[i] = inv[i] * inv[i-1] % mod;//m!关于mod的逆元
return (f * inv[m]) % mod;
}
//第二种
LL power(LL a, LL b, LL p) {
LL ret = 1;
while(b) {
if(b & 1) ret = ret * a % p;
a = a * a % p;
b >>= 1;
}
return ret;
}
LL f[100010];//f[i]表示 i! % p
LL C(LL n, LL m, LL p) {
if(n < m) return 0;
return (f[n] * power(f[m], p-2, p)) % p * power(f[n-m], p-2, p) % p;
}
P3807 【模板】卢卡斯定理
#include <cstdio>
typedef long long LL;
LL C(LL n, LL m, LL p) {
LL inv[100010];
inv[1] = 1;
LL f = 1;
for(int i = n - m + 1; i <= n; i++) f = f * i % p;
for(int i = 2; i <= m; i++) inv[i] = (p - p / i) * 1LL * inv[p % i] % p;
for(int i = 2; i <= m; i++) inv[i] = inv[i] * inv[i-1] % p;
return (f * inv[m]) % p;
}
LL lucas(LL n, LL m, LL p) {
if(m == 0) return 1LL;
return (C(n % p, m % p, p) * lucas(n / p, m / p, p)) % p;
}
int main() {
int t;
scanf("%d", &t);
while(t--) {
LL n, m, p;
scanf("%lld %lld %lld", &m, &n, &p);
n += m;
printf("%lld\n", lucas(n, m, p));
}
return 0;
}
牛牛与组合数学
#include <cstdio>
#include <cstring>
typedef long long LL;
LL inv[150000];
LL mod[10] = {88897 ,58787 ,125453 , 7853 , 78569 ,145267 ,25867 ,105899 ,8803,4591};
LL C(LL n, LL m, LL p) {
if(n < m) return 0;
LL f = 1;
inv[1] = 1;
for(int i = n-m+1; i <= n; i++) f = f * i % p;
for(int i = 2; i <= m; i++) inv[i] = (p - p / i) * 1LL * inv[p % i] % p;
for(int i = 2; i <= m; i++) inv[i] = inv[i] * inv[i-1] % p;
return ((f * inv[m]) % p);
}
LL lucas(LL n, LL m, LL p) {
if(m == 0) return 1;
return (C(n % p, m % p, p) * lucas(n / p, m / p, p)) % p;
}
char s[4000010];
int main() {
LL n, m;
scanf("%lld %lld %s", &n, &m, s);
int flag = 0;
int ns = strlen(s);
for(int j = 0; j < 10; j++) {
LL num = 0;
for(int i = 0; i < ns; i++) {
num = num * 10 + (s[i] - '0');
num = num % mod[j];
}
LL num2 = lucas(n, m, mod[j]);
if(num != num2) {
flag = 1;
printf("No!\n");
break;
}
}
if(flag == 0) printf("Yes!\n");
return 0;
}
Saving Beans HUD 3037
松鼠有不多于m颗松果(0~m),他决定放到n棵树下(有些树可以不放),问有多少种方案。结果取模p
根据插板法,\(\sum_{i=0}^mC_{n+m-1}^{n-1}\) = \(C_{n-1}^{n-1}\) + \(C_n^{n-1}\) +...+ \(C_{n+m-1}^{n-1}\)
= \(C_n^n\) + \(C_n^{n-1}\) +...+ \(C_{n+m-1}^{n-1}\)
= \(C_{n+1}^n\) + \(C_{n+1}^{n-1}\) +... + \(C_{n+m-1}^{n-1}\)
=\(C_{n+m}^n\)
(组合数推导公式:\(C_n^m\) = \(C_{n-1}^m\) + \(C_{n-1}^{m-1}\))
这里用上面第一种求C(LL a, LL b, LL p)会wa,我搞不懂QAQ
#include <cstdio>
typedef long long LL;
LL power(LL a, LL b, LL p) {
LL ret = 1;
while(b) {
if(b & 1) ret = ret * a % p;
a = a * a % p;
b >>= 1;
}
return ret;
}
LL f[100010];
LL C(LL n, LL m, LL p) {
if(n < m) return 0;
return (f[n] * power(f[m], p-2, p)) % p * power(f[n-m], p-2, p) % p;
}
LL lucas(LL n, LL m, LL p) {
if(m == 0) return 1LL;
return (C(n % p, m % p, p) * lucas(n / p, m / p, p)) % p;
}
int main() {
int t;
scanf("%d", &t);
while(t--) {
LL n, m, p;
scanf("%lld %lld %lld", &n, &m, &p);
n += m;
f[0] = f[1] = 1;
for(int i = 2; i < p; i++) f[i] = f[i-1] * i % p;
printf("%lld\n", lucas(n, m, p));
}
return 0;
}