【全程NOIP计划】组合计数选讲

组合数基础

加法原理

加法原理,总共的等于各个相互独立的相加

乘法原理

两个不相干的事情同时发生,总共的情况是两种情况相乘

抽屉原理
容斥原理
排列数

从n个中选m个,考虑顺序

总的方案数为Pnm=n!(nm)!P_n^m=\frac {n!} {(n-m)!},或者可以记录为AnmA_n^m

P5520 青原樱

思路

实际上就是两个幼苗之间至少有一个空位,然后m个幼苗之间必须有m-1个空位

所以把这m-1个位置空出来,然后剩下的位置乱插

答案就是Anm+1mA_{n-m+1}^m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
using namespace std;
int type,n,m,p;
long long A(int n,int m)
{
long long res=1%p;
for(int i=n-m+1;i<=n;i++)
res=(res*i)%p;
return res%p;
}
int main()
{
cin>>type>>n>>m>>p;
cout<<A(n-m+1,m)<<'\n';
return 0;
}
组合数

从n个选m个,不考虑顺序

总的方案数为Cnm=n!m!(nm)!C_n^m=\frac {n!}{m!(n-m)!},实际上选出的人再进行随便的排列组合,一共有m!m!种,所以也可以推出来排列数的公式,他们两个之间的关系是Cnmm!=PnmC_n^m*m!=P_n^m

组合数常用的式子
Cnm=Cn1m+Cn1m1 C_n^m=C_{n-1}^m+C_{n-1}^{m-1}
实际上这个就对应杨辉三角形

1

1 1

1 2 1

………………

我们可以知道一个二项式定理
(x+y)n=i=1nCnixiyni (x+y)^n=\sum_{i=1}^n C_n^i*x^iy^{n-i}
怎么证明?

常见的组合问题

1.20个人去霓虹玩,站成一排拍纪念照,但是anan和他的女朋友必须贴贴,请问有多少种方案

先把他们两个看成一个人

就变成了19个人拍照,站成一排的方案数量为19!19!,再排他和他女朋友的方案为2

所以总的方案数量为19!×219! \times 2

2.20个人去霓虹玩,站成一排拍纪念照,但是anan和他的女朋友吵架了不能贴贴,有多少种方案

实际上可以直接用容斥原理,用总的方案数减去问题1的方案数即可

但是怎么不用容斥原理呢?

先不考虑他们两个人

我们先把剩下18个人排好,方案数量为18!18!

然后18个之间,包括最左和最右一共有19个空,再把他和他女朋友放进去,一共有P192P_{19}^2的方案数量

所以总的方案数量就为18!×P19218! \times P_{19}^2

3.Zcysky大王有n个相同的纸片人老婆,他准备把这些老婆分给m个不同的手下,Zcysky秉持着雨露均沾的原则,每个人至少能分到一个纸片人老婆,一共有多少种方案

使用隔板法,实际上就是在n-1个空里面选m-1个空插,当然,隔板是一样的,所以不用考虑顺序,也就是最后总的方案数为Cn1m1C_{n-1}^{m-1}

4.Zcysky大王有n个相同的纸片人老婆,他准备把这些老婆分给m个不同的手下,但是他早就看某些人不爽了,所以有些人可能一个人都分不到,一共有多少种方案

还是隔板法,但是有可能两个隔板之间一个纸片人都没有。每个人再发一个老婆,所以一共有n+m-1个空,然后选m-1个插,所以总的方案数量为Cn+m1m1C_{n+m-1}^{m-1}

Lucas定理
卡特兰数

h(n)=h(0)h(n1)+h(1)h(n2)++h(n1)h(0)=h(i)h(ni1) h(n)=h(0)*h(n-1)+h(1)*h(n-2)+……+h(n-1)*h(0) \\=\sum h(i)*h(n-i-1)

有什么作用呢?

n对括号匹配的数目

n个节点组成二叉树

n边形三角划分

n个点依次进出栈的出战顺序

n*n的矩阵从左下角到右上角不经过对角线

求卡特兰数有两种方式
h(n)=C2nnC2nn+1h(n)=C2nnn+1 h(n)=C_{2n}^n-C_{2n}^{n+1} \\h(n)=\frac {C_{2n}^n}{n+1}

Monochromatic Triangles

题目

空间中有n个点,m条红边,任意3个点不共线

每两个点用红线或者蓝线相连,如果一个三角形的三边颜色相同,那么称为同色三角形

给你一组数据,计算同色三角形的总数

思路

根据容斥原理

同色三角形=三角形总数-不同色三角形数量

则三角形的总数量就为Cn3C_n^3

对于一个点,如果连出去d[i]d[i]条红边,就有nd[i]1n-d[i]-1条蓝边

根据乘法原理,它贡献的三角形数量就是d[i](nd[i]1)d[i]*(n-d[i]-1)

由于一个异色三角形会被计算两遍

答案就是Cn3d[i](nd[i]1)2C_n^3-\sum \frac{d[i]*(n-d[i]-1)}2

P3166 数三角形

题目

给定一个n*m的网络,请计算三点都在格点上的三角形一共有多少个

注意三角形的三点不能共线

思路

任选三个点的方案数减去三点共线的方案数量

任选三个点方案数是Cnm3C_{n*m}^3

三点共线的方案数:同一排,同一列,同一斜线

对于同一斜线的可以枚举两个端点,中间的用gcd计算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
using namespace std;
long long n,m;
long long ans;
long long gcd(long long a,long long b)
{
return b? gcd(b,a%b):a;
}
int main()
{
cin>>n>>m;
n++,m++;
ans=(n*m)*(n*m-1)*(n*m-2)/6;//三个点选择的计算公式
if(n>=3)
ans-=(n*(n-1)*(n-2)*m/6);
if(m>=3)
ans-=(m*(m-1)*(m-2)*n/6);
for(int i=1;i<n;i++)
{
for(int j=1;j<m;j++)//枚举斜线,向左斜一遍,向右斜一遍,拒绝早恋
ans-=(n-i)*(m-j)*(gcd(i,j)-1)*2;
}
cout<<ans<<'\n';
return 0;
}

P4478 上学路线

题目

从n*m个网格图的左下角走到右上角

有t个坐标不能经过

只能向上或者向右走

问有多少种不同的走法,对p取模

思路

容斥原理Dp

先把终点当做障碍点,把所有的障碍点按x为第一关键词,y为第二关键字从小到大排序

f[i]f[i]为到达第i个障碍点且不经过任何障碍点的方案数量

G[i][j]G[i][j]表示到达从第i个障碍点到第j个障碍点的方案数量

从(x1,y1)走到(x2,y2)的方案数量为Cx2+y2x1y1x2x1C_{x2+y2-x1-y1}^{x2-x1},这个用lucas定理求就可以了

f[i]=G[0][i]G[j][i]f[j]f[i]=G[0][i]-\sum G[j][i]*f[j]

当p=1000003的时候直接求解

p=1019663265的时候先分解质因数,然后使用CRT合并

矩阵相关

矩阵的定义

mnm*n个数aij排成m行n列的数表称为m行n列的矩阵,简称mnm*n矩阵

$ A=\begin{bmatrix} a_{11} & a_{12} &\dots &a_{1n}\ a_{21} & a_{22} &\dots &a_{2n}\a_{31} & a_{32} &\dots &a_{3n}\\dots & \dots & &\dots\a_{m1} & a_{m2} &\dots &a_{mn}\ \end{bmatrix} $

m×nm \times n个数组被称为矩阵A的元素

行数与列数都等于n的矩阵称为n阶矩阵或者n阶方阵

矩阵的加减法

加法:C=A+BC=A+B,则Ci,j=Ai,j+Bi,jC_{i,j}=A_{i,j}+B_{i,j}
$
\begin{bmatrix}1 & 4 & 2 \
2 & 0 & 0 \end{bmatrix}
+
\begin{bmatrix}0 & 0 & 5 \
7 & 5 & 0 \end{bmatrix}

\begin{bmatrix} 1+0 & 4+0 & 2+5 \
2+7 & 0+5 & 0+0 \end{bmatrix}

\begin{bmatrix} 1 & 4 & 7 \
9 & 5 & 0 \end{bmatrix}
减法: 减法:C=A-B,,则C_{i,j}=A_{i,j}-B_{i,j}
\begin{bmatrix}1 & 4 & 2 \
2 & 0 & 0 \end{bmatrix}

\begin{bmatrix}0 & 0 & 5 \
7 & 5 & 0 \end{bmatrix}

\begin{bmatrix} 1-0 & 4-0 & 2-5 \
2-7 & 0-5 & 0-0 \end{bmatrix}

\begin{bmatrix} 1 & 4 & -3 \
-5 & -5 & 0 \end{bmatrix}
C,A,B$的行数相同,列数也相同

对应位置加减即可,也就是说,如果他们可以加减的话,那么他们的行数相同,列数也相同

矩阵乘法

对应的行数和对应的列数相乘再加起来
ci,j=ai,1b1,j+ai,2b2,j++ai,nbn,j=r=1nai,rbr,j c_{i,j}=a_{i,1}b_{1,j}+a_{i,2}b_{2,j}+……+a_{i,n}b_{n,j}=\sum_{r=1}^n a_{i,r}b_{r,j}

$
\begin{bmatrix}1 & 0 & 2 \
-1 & 3 & 1 \end{bmatrix}
\times
\begin{bmatrix}3 & 1 \
2 & 1 \
1 & 0 \end{bmatrix}

\begin{bmatrix} (1\times 3+ 0 \times 2+2 \times 1) &(1\times 1+ 0 \times 1+2 \times 0)\
(-1\times 3+3 \times 2+1 \times 1) &(-1\times 1+3\times 1+1 \times 0)\end{bmatrix}

\begin{bmatrix} 5 & 1 \
4 & 2 \end{bmatrix}
$

矩阵乘法的用途

优化线性递推

一般的,我们只要构造出来一个矩阵,我们可以进行矩阵加速递推

P3216 数学作业

思路

f(n)=f(n1)10k+nf(n)=f(n-1)*10^k+n

对于每一段位数相同的n,更改k的值构造矩阵

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <bits/stdc++.h>
using namespace std;
struct matrix{
long long z[3][3];
} a, b;
long long n,m,temp,pre[20];
matrix operator *(matrix a, matrix b) {
matrix c;
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
c.z[i][j]=0;

for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
for(int k=0;k<3;k++)
c.z[i][j]=(c.z[i][j]+a.z[i][k]*b.z[k][j])%m;
return c;
}
matrix ksm(matrix x, long long y)
{
if(y==1)
return x;
matrix z=ksm(x,y>>1);
z=z*z;
if(y&1)
z=z*x;
return z;
}
int main()
{
cin>>n>>m;
b.z[0][1]=b.z[0][2]=1;
a.z[1][0]=a.z[1][1]=a.z[2][1]=a.z[2][2]=1;

pre[0]=1;

for(int i=1;i<=18;i++)
pre[i]=pre[i-1]*10;

for(int i=1; ;i++)
{
a.z[0][0]=pre[i]%m;
temp=min(n,pre[i]-1)-pre[i-1]+1;
b=b*ksm(a,temp);
if(pre[i]-1>=n)
break;
}
cout<<b.z[0][0]<<'\n';
return 0;
}

矩阵行列式

定义二阶矩阵行列式

n阶矩阵行列式的和为
det(A)=ai1Ai1++ainAin=j=1naij(1)i+jdet(Aij) det(A)=a_{i1}A{i1}+……+a_{in}A_{in}=\sum_{j=1}^na_{ij}(-1)^{i+j}det(A_{ij})
行列式的性质

1.矩阵某两行(列)交换,行列式乘以-1

2.矩阵某一行(列),加上另一行列,行列式不变

Min-Max容斥

期望不满足最大最小值的那个玩意儿相乘那玩意儿

那该如何求出E(max(X,Y))E(max(X,Y))E(min(X,Y))E(min(X,Y))的值呢 ?

给定集合S,设max(s)为s中的最大值,min(s)为s中的最小值,则:

max(S)=TS(1)T1min(T)max(S)=\sum_{T\in S}(-1)^{|T|-1}min(T)

min(s)=TS(1)T1max(T)min(s)=\sum_{T\in S}(-1)^{|T|-1}max(T)

Hdu4336

题目

有n种卡片,每一秒都有Pi的概率获得一张第i种卡片,求每张卡片都至少有一张的期望时间

思路

记录max(s)为s中最后获得的那种卡片第一次获得的期望时间

min(s)为s中第一个获得的那种卡片的第一次获得的期望时间

仍然满足上列式子