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;
那么对于上面式子x^m的系数,左右两部分肯定是相等的,左边系数C(n, m) , 而m = m/p*p+b0, 那么i和j分别对应m/p, 和bo
所以就可以得到证明:C(n, m) = C(n%p, m%p)*C(n/p, m/p) (mod p)。
下面就是具体题目了:
HDU 3037 Saving Beans
http://acm.hdu.edu.cn/showproblem.php?pid=3037
1 #include <iostream> 2 #include <string.h> 3 #include <stdio.h> 4 using namespace std; 5 6 #define N 100010 7 8 long long mod_pow(int a,int n,int p) 9 { 10 long long ret=1; 11 long long A=a; 12 while(n) 13 { 14 if (n & 1) 15 ret=(ret*A)%p; 16 A=(A*A)%p; 17 n>>=1; 18 } 19 return ret; 20 } 21 22 long long factorial[N]; 23 24 void init(long long p) 25 { 26 factorial[0] = 1; 27 for(int i = 1;i <= p;i++) 28 factorial[i] = factorial[i-1]*i%p; 29 //for(int i = 0;i < p;i++) 30 //ni[i] = mod_pow(factorial[i],p-2,p); 31 } 32 33 long long Lucas(long long a,long long k,long long p) //求C(n,m)%p p最大为10^5。a,b可以很大! 34 { 35 long long re = 1; 36 while(a && k) 37 { 38 long long aa = a%p;long long bb = k%p; 39 if(aa < bb) return 0; //这个是最后的改动! 40 re = re*factorial[aa]*mod_pow(factorial[bb]*factorial[aa-bb]%p,p-2,p)%p;//这儿的求逆不可先处理 41 a /= p; 42 k /= p; 43 } 44 return re; 45 } 46 47 int main() 48 { 49 int t; 50 cin >> t; 51 while(t--) 52 { 53 long long n,m,p; 54 cin >> n >> m >> p; 55 init(p); 56 cout << Lucas(n+m,m,p) << " "; 57 } 58 return 0; 59 }
分析:
题意容易转化为x1+x2+...xn <= m的解,最后结果就是C(n+m, m),但为什么呢?
我们可以这样分析:
每棵树上能够放的豆子数目i,然后用一个式子x^i表示,然后每棵树有0, 1, 2, m种可能的放置数目,用一个多项式(1+x^1+x^2+.....x^m)表示,等比数列求和(1-x^(m+1)/(1-x),n棵树就是n次方。
即问题变成了,[1-x^(m+1)]^n*1/(1-x)^n的i<=m 的各个x^i的系数之和, 对于1/(1-x)^n, 借用无穷级数的直接结论 = ∑C(n+k-1, n-1)x^k , k >= 0,很显然前面部分[1-x^(m+1)]^n只能为最小幂次也就是x^0,后面就是x^i,问题变成了,C(n+k-1, n-1) 0 <= k <= m,化简一下就是C(n+m, n).
具体求解的时候,C(n, m) = C(n%p, m%p) * C(n/p, m/p) mod p ,一旦C(x, y) x < y 那么返回0, 而且求解的时候是直接乘以阶乘的逆元。
HDU 4349 Xiao Ming's Hope
http://acm.hdu.edu.cn/showproblem.php?pid=4349
1 #include <cstdio> 2 #include <iostream> 3 #define bug(x) printf("%d ", (x)); 4 #define in freopen("solve_in.txt", "r", stdin); 5 6 using namespace std; 7 typedef long long LL; 8 int main(){ 9 10 LL n; 11 while(scanf("%I64d", &n) == 1){ 12 int x = __builtin_popcount(n); 13 LL ans = 1, a = 2; 14 while(x){ 15 if(x&1) ans = ans*a; 16 a = a*a; 17 x >>= 1; 18 } 19 printf("%I64d ", ans); 20 } 21 return 0; 22 }
分析:
题目要求C(n, i) i <= n结果中奇数个数,很明显就是sum{C(n, i)%2| i <= n}, 模2的结果是0, 1, 最终结果只有C(1, 0) , C(1, 1) , C(0, 0)为1, C(0, 1)为0,n和i的二进制表示akak-1...a2a1a0和
bkbk-1...b2b1b0中,对应位置不能出现ai = 0, bi = 1,结果为0, 其余情况结果都会为1,那么问题就转为x = n中二进制位为1个数,答案是2^x。
451E - Devu and Flowers
http://codeforces.com/problemset/problem/451/E
1 #include <cstdio> 2 #include <iostream> 3 #define bug(x) printf("%d ", (x)); 4 #define in freopen("solve_in.txt", "r", stdin); 5 using namespace std; 6 typedef long long LL; 7 8 const int maxn = 22; 9 const int M = (int)1e9+7; 10 11 LL inv[maxn], fac[maxn]; 12 LL powmod(LL a, LL b, LL c) { 13 LL res = 1; 14 while(b) { 15 if(b&1) res = res*a%c; 16 a = a*a%c; 17 b >>= 1; 18 } 19 return res; 20 } 21 void pre() { 22 fac[0] = 1; 23 for(int i = 1; i < maxn; i++) fac[i] = fac[i-1]*i%M; 24 for(int i = 1; i < maxn; i++) inv[i] = powmod(fac[i], M-2, M); 25 } 26 LL nCr(LL n, LL m) { 27 n %= M; 28 m %= M; 29 if(m == 0) return 1; 30 if(n < m) return 0; 31 LL res = 1; 32 for(int i = 0; i < m; i++) 33 res = res*(n-i)%M; 34 res = res*inv[m]%M; 35 return res; 36 } 37 LL f[maxn]; 38 void solve(LL n, LL ss) { 39 LL ans = 0; 40 for(int s = 0; s < (1<<n); s++) { 41 LL sum = 0; 42 int tag = 1; 43 for(int i = 0; i < n; i++) if(s&(1<<i)) { 44 sum += f[i]; 45 if(sum > ss) break; 46 tag *= -1; 47 } 48 49 if(sum <= ss){ 50 ans = (ans+nCr(n+ss-sum-1, n-1)*tag+M)%M; 51 } 52 } 53 if(ans < 0) ans += M; 54 printf("%I64d ", ans); 55 } 56 57 int main() { 58 59 pre(); 60 LL n, s; 61 scanf("%I64d%I64d", &n, &s); 62 for(int i = 0; i < n; i++) scanf("%I64d", f+i), f[i]++; 63 solve(n, s); 64 return 0; 65 }
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)很容易求得。