求组合数Ⅰ
题解
基于数据范围,我们可以先把相应数据初始化出来。
存在如下公式:
C
a
b
=
C
a
−
1
b
+
C
a
−
1
b
−
1
C_a^b=C_{a-1}^b+C_{a-1}^{b-1}
Cab=Ca−1b+Ca−1b−1
从
a
a
a个人中选
b
b
b个人出来,可以分为包含小明和不包含小明的情况。
包含小明:
C
a
−
1
b
−
1
C_{a-1}^{b-1}
Ca−1b−1;不包含小明:
C
a
−
1
b
C_{a-1}^b
Ca−1b
相当于Dp状态转移的过程
import java.io.*;
public class Main{
static int N=2010;
static int mod=(int)(1e9+7);
static int[][] c=new int[N][N];
public static void main(String[] args)throws IOException{
BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
int n=Integer.parseInt(in.readLine());
init();
String[] strs;
for(int i=0;i<n;i++){
strs=in.readLine().split(" ");
int a=Integer.parseInt(strs[0]);
int b=Integer.parseInt(strs[1]);
System.out.println(c[a][b]);
}
}
static void init(){
for(int i=0;i<N;i++){
for(int j=0;j<=i;j++){
if(j==0)c[i][j]=1; //从i个中选0个有1种方案
else c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
}
}
}
}
求组合数Ⅱ
题解
基于本题
a
,
b
a,b
a,b的数据范围,
1
≤
b
≤
a
≤
1
0
5
1 \leq b \leq a \leq 10^5
1≤b≤a≤105,我们可以先把相应数据初始化出来。
用如下公式将数据提前初始化:
C
a
b
=
a
!
(
b
−
a
)
!
b
!
C_a^b=\frac{a!}{(b-a)!b!}
Cab=(b−a)!b!a!
因为
a
!
m
o
d
p
=
1
m
o
d
p
×
2
m
o
d
p
×
3
m
o
d
p
×
.
.
.
×
(
a
−
1
)
m
o
d
p
×
a
m
o
d
p
a! mod p=1modp \times 2modp \times 3modp \times ... \times (a-1)modp \times amodp
a!modp=1modp×2modp×3modp×...×(a−1)modp×amodp
但是
a
!
m
o
d
p
b
!
m
o
d
p
≠
a
!
b
!
m
o
d
p
\frac{a!modp}{b!modp} \not= \frac{a!}{b!}modp
b!modpa!modp=b!a!modp
这里可以用逆元来解决这个问题
a
!
b
!
m
o
d
p
=
a
!
b
!
−
1
m
o
d
p
\frac{a!}{b!}modp=a!b!^{-1}modp
b!a!modp=a!b!−1modp
逆元也存在以下情况:
b
!
−
1
m
o
d
p
=
1
−
1
m
o
d
p
×
2
−
1
m
o
d
p
×
3
−
1
m
o
d
p
×
.
.
.
×
(
b
−
1
)
−
1
m
o
d
p
×
b
−
1
m
o
d
p
b!^{-1}modp=1^{-1}modp \times 2^{-1}modp \times 3^{-1}mod p \times ... \times (b-1)^{-1}mod p \times b^{-1}modp
b!−1modp=1−1modp×2−1modp×3−1modp×...×(b−1)−1modp×b−1modp
证明,假设
a
a
a是
x
x
x的逆元,
a
x
=
1
ax=1
ax=1,
b
b
b是
y
y
y的逆元,
b
y
=
1
by=1
by=1,所以
a
b
x
y
=
1
abxy=1
abxy=1,所以
a
b
ab
ab是
x
y
xy
xy的逆元
求逆元可看:
https://blog.csdn.net/m0_54136420/article/details/128311218?spm=1001.2014.3001.5502
import java.io.*;
public class Main{
static int N=100010;
static long[] fact=new long[N];
static long[] infact=new long[N];
static int mod=(int)(1e9+7);
public static void main(String[] args)throws IOException{
BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
fact[0]=1;
infact[0]=1;
for(int i=1;i<N;i++){
fact[i]=fact[i-1]*i%mod;
infact[i]=infact[i-1]*qmi((long)i,mod-2,mod)%mod;
}
String[] strs=in.readLine().split(" ");
int n=Integer.parseInt(strs[0]);
for(int i=1;i<=n;i++){
strs=in.readLine().split(" ");
int a=Integer.parseInt(strs[0]);
int b=Integer.parseInt(strs[1]);
System.out.println(fact[a]*infact[a-b]%mod*infact[b]%mod);
}
}
static long qmi(long a,int k,int p){
long res=1;
while(k!=0){
if((k&1)==1)res=(res*a)%p;
k>>=1;
a=a*a%p;
}
return res;
}
}
求组合数Ⅲ
题解
基于本题的数据范围。可以考虑用卢卡斯定理
卢卡斯定理公式:
C
a
b
≡
C
a
m
o
d
p
b
m
o
d
p
⋅
C
a
/
p
b
/
p
(
m
o
d
p
)
C_a^b \equiv C_{a mod p}^{b mod p} \cdot C_{a/p}^{b/p}(mod p)
Cab≡Camodpbmodp⋅Ca/pb/p(modp)
证明:
令
a
=
a
k
⋅
p
k
+
a
k
−
1
⋅
p
k
−
1
+
⋯
+
a
0
⋅
p
0
a=a_k \cdot p^k+a_{k-1} \cdot p^{k-1}+ \cdots+a_0 \cdot p^0
a=ak⋅pk+ak−1⋅pk−1+⋯+a0⋅p0 ①
b
=
b
k
⋅
p
k
+
b
k
−
1
⋅
p
k
−
1
+
⋯
+
b
0
⋅
p
0
b=b_k \cdot p^k+b_{k-1} \cdot p^{k-1}+ \cdots+b_0 \cdot p^0
b=bk⋅pk+bk−1⋅pk−1+⋯+b0⋅p0 ②
(
1
+
x
)
p
=
C
p
0
⋅
1
+
C
p
1
x
+
C
p
2
x
2
+
⋯
+
C
p
p
x
p
(1+x)^p=C_p^0 \cdot 1+C_p^1x+C_p^2x^2+\cdots+C_p^px^p
(1+x)p=Cp0⋅1+Cp1x+Cp2x2+⋯+Cppxp ③
因为
C
p
1
,
C
p
2
,
.
.
.
,
C
p
p
−
1
C_p^1,C_p^2,...,C_p^{p-1}
Cp1,Cp2,...,Cpp−1中分子都含有
p
p
p,所以mod p都等于0
故
(
1
+
x
)
p
=
C
p
0
⋅
1
+
C
p
1
x
+
C
p
2
x
2
+
⋯
+
C
p
p
x
p
≡
1
+
x
p
(
m
o
d
p
)
(1+x)^p=C_p^0 \cdot 1+C_p^1x+C_p^2x^2+\cdots+C_p^px^p \equiv 1+x^p(mod p)
(1+x)p=Cp0⋅1+Cp1x+Cp2x2+⋯+Cppxp≡1+xp(modp)
(
1
+
x
)
a
=
(
1
+
x
)
a
0
⋅
(
(
1
+
x
)
p
)
a
1
⋅
(
(
1
+
x
)
p
2
)
a
2
⋯
(
(
1
+
x
)
p
k
)
a
k
(1+x)^a=(1+x)^{a_0} \cdot ((1+x)^p)^{a_1} \cdot ((1+x)^{p^2})^{a_2}\cdots ((1+x)^{p^k})^{a_k}
(1+x)a=(1+x)a0⋅((1+x)p)a1⋅((1+x)p2)a2⋯((1+x)pk)ak
≡
(
1
+
x
)
a
0
⋅
(
1
+
x
p
)
a
1
⋅
(
1
+
x
p
2
)
a
2
⋯
(
1
+
x
p
k
)
a
k
(
m
o
d
p
)
\equiv (1+x)^{a_0} \cdot (1+x^p)^{a_1}\cdot(1+x^{p^2})^{a_2}\cdots(1+x^{p^k})^{a_k}(mod p)
≡(1+x)a0⋅(1+xp)a1⋅(1+xp2)a2⋯(1+xpk)ak(modp)
(
1
+
x
)
a
(1+x)^a
(1+x)a中包含
x
b
x^b
xb的常数项为
C
a
b
C_a^b
Cab
根据②和③可得,该
(
1
+
x
)
a
0
⋅
(
1
+
x
p
)
a
1
⋅
(
1
+
x
p
2
)
a
2
⋯
(
1
+
x
p
k
)
a
k
(1+x)^{a_0} \cdot (1+x^p)^{a_1}\cdot(1+x^{p^2})^{a_2}\cdots(1+x^{p^k})^{a_k}
(1+x)a0⋅(1+xp)a1⋅(1+xp2)a2⋯(1+xpk)ak中包含
x
b
x^b
xb的常数项是
C
a
k
b
k
⋅
C
a
k
−
1
b
k
−
1
⋯
C
a
0
b
0
(
m
o
d
p
)
C_{a_k}^{b_k} \cdot C_{a_{k-1}}^{b_{k-1}} \cdots C_{a_0}^{b_0}(mod p)
Cakbk⋅Cak−1bk−1⋯Ca0b0(modp)
综上
C
a
b
≡
C
a
k
b
k
⋅
C
a
k
−
1
b
k
−
1
⋯
C
a
0
b
0
(
m
o
d
p
)
C_a^b\equiv C_{a_k}^{b_k} \cdot C_{a_{k-1}}^{b_{k-1}} \cdots C_{a_0}^{b_0}(mod p)
Cab≡Cakbk⋅Cak−1bk−1⋯Ca0b0(modp)
即
C
a
b
≡
C
a
m
o
d
p
b
m
o
d
p
⋅
C
a
/
p
b
/
p
(
m
o
d
p
)
C_a^b\equiv C_{a mod p}^{b mod p} \cdot C_{a/p}^{b/p}(mod p)
Cab≡Camodpbmodp⋅Ca/pb/p(modp)
不清楚
C
a
b
≡
C
a
k
b
k
⋅
C
a
k
−
1
b
k
−
1
⋯
C
a
0
b
0
(
m
o
d
p
)
C_a^b\equiv C_{a_k}^{b_k} \cdot C_{a_{k-1}}^{b_{k-1}} \cdots C_{a_0}^{b_0}(mod p)
Cab≡Cakbk⋅Cak−1bk−1⋯Ca0b0(modp)和
C
a
b
≡
C
a
m
o
d
p
b
m
o
d
p
⋅
C
a
/
p
b
/
p
(
m
o
d
p
)
C_a^b\equiv C_{a mod p}^{b mod p} \cdot C_{a/p}^{b/p}(mod p)
Cab≡Camodpbmodp⋅Ca/pb/p(modp)为啥相等的,看下面:
根据①
a
=
a
k
⋅
p
k
+
a
k
−
1
⋅
p
k
−
1
+
⋯
+
a
0
⋅
p
0
a=a_k \cdot p^k+a_{k-1} \cdot p^{k-1}+ \cdots+a_0 \cdot p^0
a=ak⋅pk+ak−1⋅pk−1+⋯+a0⋅p0得,
a
m
o
d
p
=
a
0
a mod p=a_0
amodp=a0
同理
b
m
o
d
p
=
b
0
b mod p=b_0
bmodp=b0,所以
C
a
m
o
d
p
b
m
o
d
p
=
C
a
0
b
0
C_{a mod p}^{b mod p}=C_{a_0}^{b_0}
Camodpbmodp=Ca0b0
然后相当于
C
a
b
=
C
a
0
b
0
⋅
C
a
/
p
b
/
p
(
m
o
d
p
)
C_a^b=C_{a_0}^{b_0}\cdot C_{a/p}^{b/p}(mod p)
Cab=Ca0b0⋅Ca/pb/p(modp),
C
a
/
p
b
/
p
=
C
a
/
p
m
o
d
p
b
/
p
m
o
d
p
⋅
C
a
/
p
2
b
/
p
2
C_{a/p}^{b/p}=C_{a/pmodp}^{b/pmodp}\cdot C_{a/p^2}^{b/p^2}
Ca/pb/p=Ca/pmodpb/pmodp⋅Ca/p2b/p2
根据①得
C
a
/
p
m
o
d
p
b
/
p
m
o
d
p
=
C
a
1
b
1
C_{a/pmodp}^{b/pmodp}=C_{a_1}^{b_1}
Ca/pmodpb/pmodp=Ca1b1,所以
C
a
b
=
C
a
0
b
0
⋅
C
a
1
b
1
⋅
C
a
/
p
2
b
/
p
2
C_a^b=C_{a_0}^{b_0}\cdot C_{a_1}^{b_1} \cdot C_{a/p^2}^{b/p^2}
Cab=Ca0b0⋅Ca1b1⋅Ca/p2b/p2,不断递归,最后得到:
C
a
b
≡
C
a
k
b
k
⋅
C
a
k
−
1
b
k
−
1
⋯
C
a
0
b
0
(
m
o
d
p
)
C_a^b\equiv C_{a_k}^{b_k} \cdot C_{a_{k-1}}^{b_{k-1}} \cdots C_{a_0}^{b_0}(mod p)
Cab≡Cakbk⋅Cak−1bk−1⋯Ca0b0(modp)
import java.io.*;
public class Main{
static long p;
public static void main(String[] args)throws IOException{
BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
String[] strs=in.readLine().split(" ");
int n=Integer.parseInt(strs[0]);
for(int i=0;i<n;i++){
strs=in.readLine().split(" ");
long a=Long.parseLong(strs[0]);
long b=Long.parseLong(strs[1]);
p=Long.parseLong(strs[2]);
System.out.println(lucas(a,b));
}
}
static long lucas(long a,long b){
if(a<p&&b<p)return C(a,b)%p;
return (C(a%p,b%p)*lucas(a/p,b/p))%p;
}
static long C(long a,long b){
if(a<b)return 0;
long res=1;
for(long i=1,j=a;i<=b;i++,j--){
res=res*j%p;
res=res*qmi(i,p-2)%p;
}
return res;
}
static long qmi(long a,long k){
long res=1;
while(k!=0){
if((k&1)==1)res=res*a%p;
k>>=1;
a=a*a%p;
}
return res;
}
}
求组合数Ⅳ
题解
C
a
b
=
a
×
(
a
−
1
)
×
.
.
.
×
(
a
−
b
+
1
)
b
×
(
b
−
1
)
×
.
.
.
×
1
=
a
×
(
a
−
1
)
×
.
.
.
×
(
a
−
b
+
1
)
×
(
a
−
b
)
!
b
×
(
b
−
1
)
×
.
.
.
×
1
×
(
a
−
b
)
!
=
a
!
b
!
(
a
−
b
)
!
分解质因数
:
C
a
b
=
p
1
a
1
p
2
a
2
⋯
p
k
a
k
求
a
!分解
p
的次数
s
u
m
[
p
]
=
⌊
a
p
⌋
+
⌊
a
p
2
⌋
+
⌊
a
p
3
⌋
+
⋯
=
p
的倍数在
[
1
,
a
]
中的个数
+
p
2
的倍数在
[
1
,
a
]
中的个数
+
.
.
.
比如求
8
!
=
1
×
2
×
3
×
4
×
5
×
6
×
7
×
8
中
2
的个数。
1
到
8
之间
2
的一次方有
2
、
4
、
6
、
8
四个
2
含
2
的二次方有
4
、
8
,但
4
、
8
中
2
的一次方已经记过一次了
,
所以只加二次方中的两个
2
含
2
的三次方有
8
,但
8
中的一次、二次方都被记过一次了,所以只加二次方中的一个
2
根据消除质因数,
C
a
b
中
p
的出现次数为:
a
!中出现的
p
的次数
−
(
a
−
b
)
!中出现
p
的次数
−
b
!
中
p
出现的次数
\begin{aligned} C_a^b&=\frac{a \times (a-1) \times ... \times (a-b+1)}{b \times (b-1) \times ... \times 1} \\ &= \frac{a \times (a-1) \times ... \times (a-b+1) \times (a-b)!}{b \times (b-1) \times ... \times 1 \times (a-b)!} \\ &= \frac{a!}{b!(a-b)!} \end{aligned} \\ 分解质因数:C_a^b=p_1^{a_1}p_2^{a_2} \cdots p_k^{a_k} \\ \begin{aligned} 求a!分解p的次数sum[p]&=\lfloor \frac{a}{p} \rfloor+\lfloor \frac{a}{p^2} \rfloor+\lfloor \frac{a}{p^3} \rfloor+\cdots \\ &=p的倍数在[1,a]中的个数+p^2的倍数在[1,a]中的个数+... \end{aligned} \\ 比如求8!=1\times 2 \times 3 \times 4 \times 5 \times 6 \times 7 \times 8中2的个数。 \\ 1到8之间2的一次方有2、4、6、8四个2 \\ 含2的二次方有4、8,但4、8中2的一次方已经记过一次了,所以只加二次方中的两个2 \\ 含2的三次方有8,但8中的一次、二次方都被记过一次了,所以只加二次方中的一个2 \\ 根据消除质因数,C_a^b中p的出现次数为:\\ a!中出现的p的次数-(a-b)!中出现p的次数-b!中p出现的次数
Cab=b×(b−1)×...×1a×(a−1)×...×(a−b+1)=b×(b−1)×...×1×(a−b)!a×(a−1)×...×(a−b+1)×(a−b)!=b!(a−b)!a!分解质因数:Cab=p1a1p2a2⋯pkak求a!分解p的次数sum[p]=⌊pa⌋+⌊p2a⌋+⌊p3a⌋+⋯=p的倍数在[1,a]中的个数+p2的倍数在[1,a]中的个数+...比如求8!=1×2×3×4×5×6×7×8中2的个数。1到8之间2的一次方有2、4、6、8四个2含2的二次方有4、8,但4、8中2的一次方已经记过一次了,所以只加二次方中的两个2含2的三次方有8,但8中的一次、二次方都被记过一次了,所以只加二次方中的一个2根据消除质因数,Cab中p的出现次数为:a!中出现的p的次数−(a−b)!中出现p的次数−b!中p出现的次数
因为没有
m
o
d
mod
mod一个大数,所以这里要用高精度相乘,同时还要提前初始化质数。
import java.io.*;
import java.math.BigInteger;
public class Main{
static int N=5010;
static boolean[] st=new boolean[N];
static int[] primes=new int[N];
static int cnt;
static int[] sum=new int[N];
public static void main(String[] args)throws IOException{
BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
String[] strs=in.readLine().split(" ");
int a=Integer.parseInt(strs[0]);
int b=Integer.parseInt(strs[1]);
get_primes(a);
for(int i=0;i<cnt;i++){
int p=primes[i];
sum[i]=get(a,p)-get(a-b,p)-get(b,p);
}
BigInteger res=new BigInteger("1");
for(int i=0;i<cnt;i++){
int p=primes[i];
for(int j=0;j<sum[i];j++){
res=res.multiply(new BigInteger(String.valueOf(p)));
}
}
System.out.println(res);
}
static void get_primes(int n){
for(int i=2;i<=n;i++){
if(!st[i])primes[cnt++]=i;
for(int j=0;primes[j]<=n/i;j++){
st[primes[j]*i]=true;
if(i%primes[j]==0)break;
}
}
}
static int get(int n,int p){
int res=0;
while(n>0){
res+=n/p;
n/=p;
}
return res;
}
}
满足条件的01序列
题解
将 01 序列置于坐标系中,起点定于原点。若 0 表示向右走,11表示向上走,那么任何前缀中 0 的个数不少于 1 的个数就转化为,路径上的任意一点,横坐标大于等于纵坐标。题目所求即为这样的合法路径数量。
下图中,表示从 (0,0) 走到 (n,n) 的路径,在绿线及以下表示合法,若触碰红线即不合法。
由图可知,任何一条不合法的路径(如黑色路径),都对应一条从 (0,0) 走到 (n−1,n+1) 的一条路径(如灰色路径)。而任何一条 (0,0)走到 (n−1,n+1)的路径,也对应了一条从 (0,0)走到 (n,n)的不合法路径。
来源:https://www.acwing.com/solution/content/8907/
卡特兰数:
C
2
n
n
−
C
2
n
n
−
1
=
(
2
n
)
!
n
!
n
!
−
(
2
n
)
!
(
n
−
1
)
!
(
n
+
1
)
!
=
(
2
n
)
!
(
n
+
1
)
−
(
2
n
)
!
n
(
n
+
1
)
!
n
!
=
(
2
n
)
!
(
n
+
1
)
!
n
!
=
1
n
+
1
(
2
n
)
!
n
!
n
!
=
1
n
+
1
C
2
n
n
\begin{aligned} C_{2n}^n-C_{2n}^{n-1}&=\frac{(2n)!}{n!n!}-\frac{(2n)!}{(n-1)!(n+1)!} \\ &=\frac{(2n)!(n+1)-(2n)!n}{(n+1)!n!} \\ &=\frac{(2n)!}{(n+1)!n!} \\ &=\frac{1}{n+1} \frac{(2n)!}{n!n!} \\ &=\frac{1}{n+1} C_{2n}^n \\ \end{aligned}
C2nn−C2nn−1=n!n!(2n)!−(n−1)!(n+1)!(2n)!=(n+1)!n!(2n)!(n+1)−(2n)!n=(n+1)!n!(2n)!=n+11n!n!(2n)!=n+11C2nn
import java.io.*;
public class Main{
static int mod=(int)(1e9+7);
public static void main(String[] args)throws IOException{
BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
long n=Integer.parseInt(in.readLine());
long res=1;
for(long i=2*n,j=1;j<=n;j++,i--){
res=res*i%mod;
res=res*get(j,mod-2,mod)%mod;
}
System.out.println(res*get(n+1,mod-2,mod)%mod);
}
static long get(long a,long k,long p){
long res=1;
while(k>0){
if((k&1)==1)res=res*a%p;
k>>=1;
a=a*a%p;
}
return res;
}
}