组合数模板
注意复杂度, O ( N 2 ) O(N^2) O(N2),并且求解范围要求n,m小于2000。
import java.util.Scanner;
public class Main{
static final int N=2005;
static final int mod=(int)1e9+7;
static int c[][]=new int[N][N];
public static void main(String[] args) {
Scanner scan=new Scanner(System.in);
=====预处理===
for(int i=0;i<N;i++)
for(int j=0;j<=i;j++)
if(j==0) c[i][j]=1;
else c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
=======================================
int t=scan.nextInt();
while(t-->0){
int a=scan.nextInt();
int b=scan.nextInt();
System.out.println(c[a][b]);
}
}
}
快速幂
快速幂:将幂指数b转换为二进制,例:6对应二进制为110,此时 a b = a 4 ∗ a 2 ∗ a 0 a^b =a^4*a^2*a^0 ab=a4∗a2∗a0,时间复杂度为 l g n lgn lgn.
static long mod = 100000007;
static long quick_pow(long a,long b){
long res=1;
while(b>0){
if((b&1)==1) res=res*a%mod;
a=a*a%mod; // a翻倍
b>>=1; // 移位
}
return res%mod;
}
利用逆元的组合数模板
注意时间复杂度 O ( n l g n ) O(nlgn) O(nlgn), 范围n,m 可以达到 1 0 5 10^5 105。
使用了费马小定理的, a − 1 = a p − 2 m o d ( p ) a^{-1} = a^{p-2}mod(p) a−1=ap−2mod(p).并且在求解幂次方面使用了快速幂。
import java.util.Scanner;
public class Main{
static final int N=100005;
static final int mod=(int)1e9+7;
static long fact[]=new long[N];
static long infact[]=new long[N];
static long quick_pow(long a,long b){
long res=1;
while(b>0){
if((b&1)==1) res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res%mod;
}
public static void main(String[] args) {
Scanner scan=new Scanner(System.in);
fact[0]=infact[0]=1;
for(int i=1;i<N;i++){
fact[i]=fact[i-1]*i%mod;
infact[i]=quick_pow(fact[i],mod-2)%mod;
}
int t=scan.nextInt();
while(t-->0){
int a=scan.nextInt();
int b=scan.nextInt();
System.out.println(fact[a]*infact[a-b]%mod*infact[b]%mod);
}
}
}
Lucas定理的组合数模板
特点是n,m极大,因此无法直接计算,但是题目要求的只是对一个比较小的数取模。因此可以使用lucas定理简化。
Lucas定理:
时间复杂度降低为
O
(
P
l
g
P
l
g
N
)
O(PlgPlgN)
O(PlgPlgN)
import java.util.Scanner;
public class Main{
static int p;
//快速幂
static long quick_pow(long a,long b){
long res=1;
while(b>0){
if((b&1)==1) res=res*a%p;
a=a*a%p;
b>>=1;
}
return res;
}
//根据组合数定义求C(a,b) 利用了快速幂。
static long C(long a,long b){
long res=1;
for(long i=1,j=a;i<=b;i++,j--){
res=res*j%p;
res=res*quick_pow(i,p-2)%p;
}
return res;
}
//卢卡斯定理
static long lucas(long a,long b){
if(a<p && b<p) return C(a,b);
return C(a%p,b%p)*lucas(a/p,b/p)%p;
}
public static void main(String[] args) {
Scanner scan=new Scanner(System.in);
int t=scan.nextInt();
while(t-->0){
long a=scan.nextLong();
long b=scan.nextLong();
p=scan.nextInt();
System.out.println(lucas(a,b));
}
}
}
判断组合数奇偶
也是可以很简单的判断,对于 C n m C_n^m Cnm,如果(n&m) == m则为奇数,否则偶数