快速幂和求组合数
Posted lixyuan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了快速幂和求组合数相关的知识,希望对你有一定的参考价值。
- 预备知识快速幂
计算
[a^kmod p
]
快速幂就是快速算底数的k
次幂,时间复杂度为O(logk)
与朴素算法相比效率极大提升。
原理
将k
转化成二进制数
11
的二进制数为1011
则十进制11
写成
[11=1 imes2^3+0 imes2^2+1 imes2^1+1 imes2^0
]
当k=11
时,
将(a^k=a^{11})写成
[a^{2^0} imes a^{2^1} imes a^{2^3}
]
代码实现:
int qmi(int a, int k, int p) {
long res = 1, base = a;
while(k != 0) {
if((k & 1) == 1) res = res * a % p;
k >>= 1;
a = a * a % p;
}
return (int) res;
}
求组合数
- 定义法
[C_{n}^{m}=cfrac {n imes(n-1).. imes(n-m+1)}{m!}=cfrac{n!}{m!(n-m)!}=prod_{i=1}^{m}cfrac{n-m+i}{i}
]
int comb01(int n, int m) {
long res = 1;
for(int i=1; i <= m; i++) {
res = res * (n-m+i) / i;
}
return (int) res;
}
2.性质法
[C_n^m=C_{n-1}^m+C_{n-1}^{m-1}
]
一个物品有两种选择,要么选要么不选,n个中选m个的方案数((C_n^m))等于当前一个物品不选的方案数((C_{n-1}^{m}))和选当前物品的方案数((C_{n-1}^{m-1}))的和。
int comb02(int n, int m, int p) {
int[][] c = new int[n+1][m+1];
for(int i=0; i <= n; i++) {
for(int j=0; j <= i && j <= m; j++) {
if(j==0) c[i][j] = 1;
else c[i][j] = (c[i-1][j-1] + c[i-1][j]) % p;
}
}
return c[n][m];
}
3.预处理逆元法
预处理出阶乘取模的余数fact[N],和阶乘取模的逆元infact[N]
如果取模的数是质数,可以使用费马小定理求逆元
费马小定理:若p为素数,则有(a^{p-1}equiv1 (mod p))
即:
[a imes a^{p-2} equiv 1 (mod p)
]
(a^{p-2})就是a在(mod p)意义下的逆元。
如果多次计算
long[] fact, infact;
int N;
int p = 1000000007;//必须为素数
void init(int n, int p){
N = 1001;// 必须N大于恒大于n
fact = new long[N];
infact = new long[N];
fact[0] = infact[0] = 1;
for(int i=1; i < N; i++) {
fact[i] = fact[i-1]*i % p;
infact[i] = qmi((int)fact[i], p-2, p);
}
//System.out.println(Arrays.toString(fact));
//System.out.println(Arrays.toString(infact));
}
int comb03(int n, int m, int p) {
init(p);
long res = fact[n] * infact[n-m] % p * infact[m] % p;
return (int)res;
}
或者计算一次
int p = 1000000007;//必须为素数
long ans1 = 1L, ans2 = 1L;//组合数的分子和分母
for(int i=n; i >= n-m+1; i--)
ans1 = ans1 * i % p;
for(int i=1; i <= s; i++)
ans2 = ans2 * i % p;
ans2 = qmi((int)ans2, p-2, p);
int comb = ans1*ans2%p;
以上是关于快速幂和求组合数的主要内容,如果未能解决你的问题,请参考以下文章