您的位置:首页 > 其它

Lucas定理及其应用

2014-09-28 19:39 176 查看
Lucas定理这里有详细的证明。

其实就是针对n, m很大时,要求组合数C(n, m) % p, 一般来说如果p <= 10^5,那么就能很方便的将n,m转化为10^5以下这样就可以按照乘法逆元的方法求解。

定义:

C(n, m) = C(n%p, m%p)*C(n/p, m/p) (mod p)

一种比较好理解的证明方式是这样的, 上面资料中有提到,

由p为质数,(1+x)^p = 1+x^p (mod p) p为质数,然后就是下面这幅图的内容了。

将n, m分别表示成p进制,n = n/p*p+a0, m = m/p*p+b0;

#include <cstdio>
#include <iostream>
#define bug(x) printf("%d\n", (x));
#define in freopen("solve_in.txt", "r", stdin);
using namespace std;
typedef long long LL;

const int maxn = 22;
const int M = (int)1e9+7;

LL inv[maxn], fac[maxn];
LL powmod(LL a, LL b, LL c) {
LL res = 1;
while(b) {
if(b&1) res = res*a%c;
a = a*a%c;
b >>= 1;
}
return res;
}
void pre() {
fac[0] = 1;
for(int i = 1; i < maxn; i++) fac[i] = fac[i-1]*i%M;
for(int i = 1; i < maxn; i++) inv[i] = powmod(fac[i], M-2, M);
}
LL nCr(LL n, LL m) {
n %= M;
m %= M;
if(m == 0) return 1;
if(n < m) return 0;
LL res = 1;
for(int i = 0; i < m; i++)
res = res*(n-i)%M;
res = res*inv[m]%M;
return res;
}
LL f[maxn];
void solve(LL n, LL ss) {
LL ans = 0;
for(int s = 0; s < (1<<n); s++) {
LL sum = 0;
int tag = 1;
for(int i = 0; i < n; i++) if(s&(1<<i)) {
sum += f[i];
if(sum > ss) break;
tag *= -1;
}

if(sum <= ss){
ans = (ans+nCr(n+ss-sum-1, n-1)*tag+M)%M;
}
}
if(ans < 0) ans += M;
printf("%I64d\n", ans);
}

int main() {

pre();
LL n, s;
scanf("%I64d%I64d", &n, &s);
for(int i = 0; i < n; i++) scanf("%I64d", f+i), f[i]++;
solve(n, s);
return 0;
}


View Code
n个盒子每个盒子fi朵鲜花,选出s朵花,共有多少种选法?

分析:

每个盒子用(1+x^1+x^2..........x^fi)表示,总表达式∏(1+x^1+x^2..........x^fi) (0 <= i < n) = ∏(1-x^(fi+1)(0 <= i < n) * 1/(1-x)^n, 答案就是x^s的系数。

同样分成两部分 ∏(1-x^(fi+1)(0 <= i < n)和 1/(1-x)^n,后一部分很好求, x^k系数就是 C(n+k-1, n-1),前一部分x^i通过二进制表示法求出, 二进制每个位为1则表示拿出-x^(fi+1)相乘。那么问题关键在于C(n+k-1, n-1),对于C(n, m) = C(n%p, m%p)*C(n/p, m/p) mod p 由于m很小C(n/p, m/p)为1,那么C(n%p, m%p)很容易求得。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: