排列数

nn 个数字中 选 mm 个,构成一个排列,问方案数

Pnm=n×(n1)×(n2)××(nm+1)P_n^m = n \times (n-1) \times (n-2) \times \cdots \times (n-m+1)

Pnn=n!=n×(n1)×(n2)××1P_n^n = n! = n \times (n-1) \times (n-2) \times \cdots \times 1

组合数

https://en.wikipedia.org/wiki/Combination

(nm)=Cnm=n×(n1)×(n2)××(nm+1)/m!\binom{n}{m} = C_n^m = n \times (n-1) \times (n-2) \times \cdots \times (n-m+1) / m!

求组合数的方法

直接杨辉三角递推

Cnm=Cn1m+Cn1m1C_n^m = C_{n-1}^{m} + C_{n-1}^{m-1}

Cnm=CnnmC_n^m = C_n^{n-m}

如果m<0m < 0m>nm > n 那么 Cnm=0C_n^m = 0

优势:可以模任意数字
优势:预处理之后,可以O(1)O(1)回答组合数

劣势:时间复杂度是O(n2)O(n^2)

int c[1020][1020];
int main()
{
	for (int i = 0; i <= n; i++)
	{
		c[i][0] = 1;
		for (int j = 1; j <= i; j++) // 如果 i==0 这层循环是不运行的
		{
			c[i][j] = c[i-1][j] + c[i-1][j-1];
		}
	}
	return 0;
}

在模大质数的情况下,可以预处理 阶乘 和 阶乘的乘法逆元

大质数:n和m小于模数
如果模小质数,需要先Lucas定理

这样可以 O(1)O(1) 得到组合数的结果

C(n, m) = fac[n] * invfac[m] % p * invfac[n - m] % p

p = 1000000007
0! = 1
1! = 1
2! = 2
3! = 6
4! = 24
5! = 120

inv(0!) = 1
inv(1!) = 1
inv(2!) = 500000004
inv(3!) = 166666668
inv(4!) = 250000002
inv(5!) = 400000003

C(5, 2) = 5! / 2! / 3! = 10

Lucas定理

小质数:n和m大于模数
组合数模小质数

中国剩余定理

如果组合数模没有平方因子的数,可以转为模质数,再用中国剩余定理合并

组合数模质数,如果n和p都很大,是不可做的

如果p很小,可以Lucas定理把n变小
C(10**9,10**8) % 10007 = ...

如果p很大,但是n很小,可以O(n)O(n)预处理阶乘,阶乘逆元

如果p很大,n也很大,那么这是不容易做的,有一个nlogn\sqrt{n} \log n 很复杂的方法求 n!modpn! \bmod p

组合数模质数次幂

转化为计算阶乘模质数次幂的余数是多少,次数是多少?

时间复杂度O(质数次幂)

(可能可以用Wilson定理优化)

乘一个除一个

优势:好写,可以计算 C(n,m)C(n, m) n很大,m很小的情况
一个变种是把 除法 换成 乘法逆元
如果使用乘法逆元,可以分别计算 分子的乘积 和 分母的乘积,最后逆元一次
劣势:慢

int C(int n, int m)
{
	int re = 1;
	for (int i = 0; i < m; i++)
	{
		re = re * (n - i) / (i + 1);
	}
	// for (int i = 1; i <= m; i++)
	// {
	// 	re = re * (n - i + 1) / i;
	// }
	return re;
}

int C(int n, int m)
{
	int x = 1;
	int y = 1;
	for (int i = 0; i < m; i++)
	{
		x = x * (n - i) % p;
		y = y * (i + 1) % p;
	}
	return x * pow(y, p - 2, p) % p;
}

C(6, 3) = 初始值 * 6 / 1 * 5 / 2 * 4 / 3

多重集的全排列

比如 3个a 4个b 全排列方案数?
C(7,3) = C(7,4) = 7! / 3! / 4!

有重复元素的全排列如何计算?
比如 3个a 4个b 5个c 全排列方案数?
C(12, 3) * C(9, 4) * C(5, 5)
C(12, 5) * C(7, 4) * C(3, 3)
12! / 3! / 4! / 5!

全排列 除以 每个元素重复次数的阶乘

多重集的组合

动态规划
一共有n种元素,第ii种元素有aia_i个,希望从所有元素中选取m个,构成一个组合,顺序不同算作相同方案,问方案数

f[i][j] 前i种元素选了j个的方案数,枚举第
i个元素选了几个

f[0][0] = 1
for (int i = 1; i <= n; i++)
{
	for (int j = 0; j <= m; j++)
	{
		for (int k = 0; k <= a[i] && k <= j; k++)
		{
			f[i][j] += f[i - 1][j - k]; // 可以前缀和优化
		}
	}
}

时间复杂度可以优化到O(nm)O(nm)

多重集的部分排列

动态规划
一共有n种元素,第ii种元素有aia_i个,希望从所有元素中选取m个,构成一个排列,顺序不同算作不同方案,问方案数

for (int i = 1; i <= n; i++)
{
	for (int j = 0; j <= m; j++)
	{
		for (int k = 0; k <= a[i] && k <= j; k++)
		{
			f[i][j] += f[i - 1][j - k] * c[j][k];
		}
	}
}

二项式定理

学会高中课本内容即可
(x+y)1=x+y(x + y)^1 = x + y
(x+y)2=x2+2xy+y2(x + y)^2 = x^2 + 2xy + y^2
(x+y)3=x3+3x2y+3xy2+y3(x + y)^3 = x^3 + 3x^2y + 3xy^2 + y^3
(x+y)n=(n0)xny0++(ni)xniyi++(nn)x0yn(x + y)^n = \binom{n}{0}x^n y^0 + \cdots + \binom{n}{i}x^{n-i}y^i + \cdots + \binom{n}{n}x^0 y^n

P2183 [国家集训队]礼物

https://www.luogu.com.cn/problem/P2183

题目背景

一年一度的圣诞节快要来到了。每年的圣诞节小 E 都会收到许多礼物,当然他也会送出许多礼物。不同的人物在小 E 心目中的重要性不同,在小 E 心中分量越重的人,收到的礼物会越多。

题目描述

小 E 从商店中购买了 nn 件礼物,打算送给 mm 个人,其中送给第 ii 个人礼物数量为 wiw_i。请你帮忙计算出送礼物的方案数(两个方案被认为是不同的,当且仅当存在某个人在这两种方案中收到的礼物不同)。由于方案数可能会很大,你只需要输出模 PP 后的结果。

输入格式

输入的第一行包含一个整数 PP,表示模数。
第二行包含两个整数 nnmm,分别表示小 E 从商店购买的礼物数和接受礼物的人数。
33 到第 (m+2)(m + 2) 行,每行一个整数,第 (i+2)(i + 2) 行的整数 wiw_i 表示送给第 ii 个人的礼物数量。

输出格式

若不存在可行方案,则输出 Impossible,否则输出一个整数,表示模 PP 后的方案数。

样例 #1

样例输入 #1
100
4 2
1
2

样例输出 #1
12

样例 #2

样例输入 #2
100
2 2
1
2
样例输出 #2
Impossible

提示

样例 1 解释
/ 分割,/ 前后分别表示送给第一个人和第二个人的礼物编号。1212 种方案详情如下:

1/23 1/24 1/34
2/13 2/14 2/34
3/12 3/14 3/24
4/12 4/13 4/23

数据规模与约定
P=i=1tpiciP= \prod_{i=1}^t p_i^{c_i}pip_i 为质数。

对于 15%15\% 的数据,n15n\leq 15m5m\leq 5pici105p_i^{c_i}\leq 10^5

在剩下的 85%85\% 数据中,约有 60%60\% 的数据满足 t2t\leq 2ci=1c_i=1pi105p_i\leq 10^5,约有 30%30\% 的数据满足 pi200p_i\leq 200

对于 100%100\% 的数据,1n1091\leq n\leq 10^91m51\leq m\leq 51pici1051\leq p_i^{c_i}\leq 10^51wiP1091\leq w_i \leq P\leq 10^9

参考代码

题解

先对PP分解质因数,分解成质数的次幂pcp^c
如果wiw_i的和大于nn,直接无解
如果wiw_i的和小于nn,加一个人
保证wiw_i的和等于nn
n!modpcn! \bmod p^c 的余数和次数

11nn中不是pp的倍数的数字的乘积模pcp^c

11nnpp的倍数,=

  1. 排列数
  2. 组合数
    1. 求组合数的方法
      1. 直接杨辉三角递推
      2. 在模大质数的情况下,可以预处理 阶乘 和 阶乘的乘法逆元
      3. Lucas定理
      4. 中国剩余定理
      5. 组合数模质数,如果n和p都很大,是不可做的
      6. 组合数模质数次幂
      7. 乘一个除一个
    2. 多重集的全排列
      1. 多重集的组合
      2. 多重集的部分排列
      3. 二项式定理
      4. P2183 [国家集训队]礼物