组合数

组合数

其实我也没想到我第一篇博客会讲一个偏数学的内容。
主要是我太弱了,只会这个

何为组合数

记号$C_n^m$表示组合数,其意义为在$n$个可区分物品中无序地选择$m$个物品的方案数。
如三个数分别为$1,2,3$,希望选出两个数,则有$(1,2)(1,3)(2,3)$三种方案。
因为是无序选择,因此$(1,2)$和$(2,1)$被认为是同一种方案。
因而$C_3^2=2$.

组合数的简单性质

根据意义,易知:
$C_n^0=1$
$C_n^1=n$
$C_n^n=1$

$C_{n}^{i} = C_{n}^{n-i}$

$C_{n}^{m} = 0 (m>n)$

$ \sum_{i=1}^{n} C_{n}^{i} = 2^{n}$

组合数的计算方法

根据组合数的定义,我们可以得到计算公式。
$$C_n^m= \frac{n(n-1) \cdots(n-m+1)}{m!}=\frac{n!}{m!(n-m)!}$$
即统计有序选择的方案数$n(n-1)\cdots(n-m+1)$,然后除掉每个方案被重复计数的$m!$。
有了这个式子后,我们只要处理出阶乘及其倒数,便可以计算组合数。
由于组合数一般很大,实际上大多时候在模意义下计算。

组合数还存在递推式。
即$$C_n^m=C_ {n-1}^{m-1}+C_ {n-1}^m$$
从组合意义来考虑该递推式——
从$n$个数中选$m$个,则考虑最后一个数是否被选,然后化为从$n−1$个数中选$m$个或$m−1$个。
该方法复杂度为$O(n^2)$,但是简单自然,且对模数无特殊要求。

模意义下的组合数计算

对质数$p$取模的组合数,是常见的组合数求解形式。
$C_n^m=\frac{n!}{m!(n-m)!}$
也就是我们只需要处理阶乘即可。
则可以处理模意义下的阶乘,由于还需要除法,需要顺便处理$\frac{1}{i!}$.
求逆元可用快速幂解决。
一般预处理只求$\frac{1}{maxn!}$,然后根据$\frac{1}{i!}=\frac{1}{(i+1)!}(i+1)$倒推即可。
相比刚才的方法, 这个方法对模数有要求,复杂度为$O(n\ log\ n)$.
那$n$和$m$都更大的情况呢?

组合数的计算方式

若计算$C_n^mmod\ p$($p$为质数),则组合数满足以下公式:
$$C_n^mmod\ p=C_ {\frac{n}{p}}^{\frac{m}{p}}C_ {n\ mod\ p}^{m\ mod\ p}$$
这被称为卢卡斯定理
运用该式可以进行递归计算,且只涉及$p$以内的组合数。

而如果要计算$C_n^m mod\ p^k$($p$为质数),
由$C_n^m=\frac{n!}{m!(n-m)!}$,易知:
我们不妨计算每个阶乘除去$p$的部分,这样分母也存在逆元。
这可以分治解决,排除$p$后,$n!$被分为若干循环外加一段,每个循环长度为$n^k$。
然后将含有$p$的部分除掉$p$化为子阶乘递归处理。
最后用库默尔定理计算含有多少$p$,当然也可以在分治过程中直接统计。
此方法适用于$p^k$不大的情况。
可以看出该方法能结合中国剩余定理推广至对一般数$p$的组合数取模(要求分解出的$p^k$都不大)。
(什么,你问我具体的公式?上面的库默尔定理有下划线发现了吗?我要是会公式我还只讲思路干嘛

例题

例1

给你两个数$n$与$m$。
统计有多少长度为$n$非负整数序列$A$,使元素和为$m$。
答案对$1000000007$取模。

$n,m\leq1000000$

思路:
将$m$加上$n$,则转化为统计正整数序列。
该问题可以这样描述:有$n$个盒子,将$m$个球放入其中使得每个盒子至少一个的方案数。
我们将球排成一行,考虑将$n-1$个隔板插入其中,只能在球与球间插入,且至多插入一个。则与上述问题等价。
从组合意义上可以得知结果为$C_{m-1}^{n-1}$.
该结论也被称作抽屉原理

例2

在一个网格图中,从$(0,0)$走到$(n,m)$,要求不能超越$y=x$这条斜线,每次往右或往上走长度为$1$,求方案数。
答案对$1000000007$取模。

$n,m\leq1000000$

思路:
(第一眼看上去是不是很像卡特兰数我刚开始也这么认为,但很抱歉,不保证$m=n$)
直接计算走到$(n,m)$的方案数,可以知道一定有$n+m$步,其中有$n$步向右,$m$步向上。
如何考虑$y=x$?
不能超越$y=x$,即不能抵达$y=x+1$。
对于每一条抵达了$y=x+1$的路径,找到最后一个交点,将之后的路径翻转,可以得到一条到$(n,m)$对称点的路径。
而一条到$(n,m)$对称点的路径,必定与$y=x+1$相交,找到最后一个交点可翻转回去。因此从$(0,0)$到$(n,m)$关于$y=x+1$的对称点的路径数等于非法路径数。

例3 CF785D Anton and School - 2

一个长度为$n$的括号序列,问有多少非空子序列长度为偶数,且前一半为左括号后一半为右括号(例如“(())”)。
答案对$1000000007$取模。

$n\leq200000$

思路:
可以枚举最后一个左括号,假如它左边有$A$个左括号,右边有$B$个右括号。
贡献为$\sum C_A{t-1}C_Bt$
然而我们无法每次枚举计算该式子。
考虑变化式子为$\sum C_A{t-1}C_B$
可考虑该式子组合意义——
有$A+B$个球排成一行,选择其中$B-1$个球的方案数。
该求和式则是在枚举前$A$个球中选$t-1$个。
即可以推广出结论——
$$\sum C_AiC_B=C_{A+B}^n$$
该式被称为范德蒙恒等式

代码(贴一下本蒟蒻的代码):

#include<bits/stdc++.h>
#define N 400010
#define MOD 1000000007

using namespace std;

int l,r,len;
long long ans;
long long fac[N],inv[N];
string str;

long long Pow(long long a,long long p) {
	a%=MOD;
	long long ans=1;
	while(p) {
		if(p&1) {
			ans=ans*a%MOD;
		}
		a=a*a%MOD;
		p>>=1;
	}
	return ans;
}

void Init() {
	fac[0]=1;
	inv[0]=1;
	for(int i=1;i<N;i++) {
		fac[i]=fac[i-1]*i%MOD;
        inv[i]=Pow(fac[i],MOD-2);
	}
	return;
}

long long Calc(int m,int n) {
	if(m<n||m<0) {
		return 0;
	}
	long long res=fac[m]*inv[m-n]%MOD*inv[n]%MOD;
	return res;
}

int main()
{
	cin>>str;
	len=str.length();
	Init();
	for(int i=0;i<len;i++) {
		if(str[i]==')') {
			r++;
		}
	}
	for(int i=0;i<len;i++) {
		if(str[i]==')') {
			r--;
		}
		else {
			ans=(ans+Calc(l+r,l+1))%MOD;
			l++;
		}
	}
	cout<<ans;
	return 0;
}

小结

组合数应用十分广泛,在信奥中常作为数论试水题出现,也经常出没在各大省选中。而在高数联中,更是作为入门第一讲(排列组合)。所以学好组合数是非常必要的。
等我有时间再写一个奥数的组合数(乱立Flag)

posted @ 2019-08-24 18:06  WalkerV  阅读(1204)  评论(0编辑  收藏  举报