组合数学基础

分类加法原理

有1类a种选择,有1类b种选择,
共有a+b种结果

分步乘法原理

有1类a种选择,有1类b种选择,2类各选一种
共有a*b种结果

减法原理

有a个数,其中有b个不具备条件的数,
共有a/b种具备条件的数

排列

有n个数排成一排
共有n*(n-1)*(n-2)*...*1=n!种不同的顺序

有n个数排成一排,选m个数排成一排
共有\(A_n^m\)=n!/(n-m)!种不同的顺序
求法

long long A(long long m,long long n,long long &mod)
{
    long long p=1,q=1;
    for(long long i=1;i<=n;i++)
    {
        p*=i;
        p%=mod;
    }
    if(m==n) return (p%mod);
    for(long long i=1;i<=n-m;i++)
    {
        q*=i;
        q%=mod;
    }
    return (p/q)%mod;
}

组合数

有n个数排成一排,选m个数,不考虑顺序
共有\(C_n^m\)=n!/m!(n-m)!种选法;
\(C_n^0\)=1
\(C_n^1\)=n
\(C_n^2\)=n(n-1)/2;
求法

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=2010;
const ll mod=998244353;
const ll SSS=2000;
ll jie[N],jie_ni[N];
ll ppow(ll n,ll m){ll s=1;while(m){if(m&1) s=s*n%mod;n=n*n%mod;m/=2;}return s;}
ll C(int n,int m){
	if(n<m||n<0||m<0)
		return 0;
	return jie[n]*jie_ni[m]%mod*jie_ni[n-m]%mod;
}
int main(){
	jie[0]=1;
	for(int i=1;i<=SSS;i++)
		jie[i]=jie[i-1]*i%mod;
	jie_ni[SSS]=ppow(jie[SSS],mod-2);
	for(int i=SSS;i>0;i--)
		jie_ni[i-1]=jie_ni[i]*i%mod;
}


求所有组合数

const int N=10000;
int c[N][N];  
ll C(ll n,ll m) {
	if(n<m||n<0||m<0)
		return 0;
	return c[n][m]%mod;
}
int main{
    for(int i=0;i<=MAX;i++){
        c[i][0]=1;
        for(int j=1;j<=i;j++){
            c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
        }
    }
{
  

插板法

用于解决一些特定的问题,

1.例如m个盒子放n个小球盒子里不为空把盒子抽象为隔板把n个球分成m份,那就有n-1个位置m-1个隔板,组合数就为\(C_{n-1}^{m-1}\)

2.若盒子可以为空,则可以假设多m个球每个盒子先放一个问题转换为1的形式为\(C_{n+m-1}^{m-1}\)
等式

对称恒等式:(杨辉三角具有对称性)

\(C_n^m\)=\(C_n^{n-m+1}\)

吸收恒等式:

\(C_n^m\)=\(n/m * C_{n-1}^{m-1}\)
移项可以得到
\(m*C_n^m=n*C_{n-1}^{m-1}\)

归纳恒等式:(其实就是杨辉三角的左右肩膀上数的和)

\(C_n^m\)=\(C_{n-1}^m + C_{n-1}^{m-1}\)

上指标求和

\(\sum_{i=0}^n C_i^m=C_{n+1}^{m+1}\)

平行求和

\(\sum_{i=0}^nC_{m+i}^i=C_{n+m+1}^n\)

行内交错和

对于\(n>0\)\(\sum_{i=0}^n(-1)^{i}C_n^i=0\)

多重集组合数

\(C_n^{x_i}C_{n-{x_1}}^{x_2}C_{n-{x_1}-{x_2}}^{x_3}...C_{c-{x_1}-{x_2}-...-{x_{k-1}}}^{x_k}={n!\over{x_1}!{x_2}!...{x_k}!}\)

二项式定理

\((a+b)^n=\sum_{i=0}^nC_n^ia^ib^{n-i}\)

范德蒙德卷积

\(\sum_{i=0}^nC_n^iC_m^{k-i}=C_{n+m}^k\)

可以认为有n个男生m个女生选k个人,可以先在男生选i个再再女生里选k-i个也可以混在一起在n+m个人里选k个人可以证明这两种方案是等价的

Lucas定理

\(C_n^m\equiv C_{n/p}^{m/p}C_{nMODp}^{mMODp}(MODp)\)
前提p为质数

相当于在把n,m,进行p进制拆解,在相同的位数若n的大小小于m的大小则\(C_n^m\)一定为0,用这种方法只需预处理0-(p-1)范围内的阶乘与逆元即可,若预处理的大于p则模p之后为0所以要用Lucas

Kummer定理

\(对于n,m,p,C_{n+m}^n中p的个数等于n+m在p进制下的进位次数\)

证明:

\(要提前知道n!含有的p的幂次为\)(用建房子的思路):

\[\sum_{1}^{\infty}\left[ n\over p^i \right] \]

然后\(C_{n+m}^m含p的幂次为\):

\[\sum_{i=1}^\infty \left[ n+m\over{p^i}\right]-\sum_{i=1}^\infty \left[ n\over{p^i}\right]-\sum_{i=1}^\infty \left[ m\over{p^i}\right] =\sum_{i=1}^\infty (\left[ n+m\over{p^i}\right]-\left[ n\over{p^i}\right]-\left[ m\over{p^i}\right]) \]

接下来只需要算出\(n+m\)在p进制下是进位次数(加法过程中)就行。

将\(n+m,n,m\)分别写成p进制(0为最低位)

\[\overline {c_0c_1c_2...c_{unknow}},\overline {n_0n_1n_2...n_{unknow}},\overline {m_0m_1m_2...m_{unknow}} \]

在加法过程中,定义第\(pos\)位发生进位的条件为:

\[n_{pos}+m_{pos}\geq p \]

若第\(pos\)位发生进位则

\[\overline {n_{pos+1}n_{pos+2}...n_{unknow}}+\overline {m_{pos+1}m_{pos+2}...m_{unknow}} <\overline {c_{pos+1}c_{pos+2}...c_{unknow}} \]

当然由于进位最多进一\((\lfloor{{(p-1)+(p+1)}\over{p}}\rfloor)\),故当第\(pos\)位发生进位时

\[\overline {c_{pos+1}c_{pos+2}...c_{unknow}}-\overline {n_{pos+1}n_{pos+2}...n_{unknow}}-\overline {m_{pos+1}m_{pos+2}...m_{unknow}} =1 \]

反之

\[\overline {c_{pos+1}c_{pos+2}...c_{unknow}}-\overline {n_{pos+1}n_{pos+2}...n_{unknow}}-\overline {m_{pos+1}m_{pos+2}...m_{unknow}} =0 \]

即

\[\overline {c_{pos+1}c_{pos+2}...c_{unknow}}=\overline {n_{pos+1}n_{pos+2}...n_{unknow}}+\overline {m_{pos+1}m_{pos+2}...m_{unknow}} \]

然后因为

\[\overline {x_{pos+1}x_{pos+2}...x_{unknow}}={x\over p^{pos+1}} \]

在p进制下\(n+m\)过程中进位次数用式子表示就是

\[\sum_{i=1}^\infty (\left[ n+m\over{p^i}\right]-\left[ n\over{p^i}\right]-\left[ m\over{p^i}\right]) \]

证明完毕

错排

表示n个元素,每个元素都不放在原来的位置

举例:

2 1 3 不合法

2 3 1合法

1:容斥

随意放-规定一本书放在了原来位置,其他随意

+规定两本书放在了原来位置,其他随意

-规定三本书放在了原来位置,其他随意

......

规定三本书放在了原来位置

D(4)=9

\(4!-C_4^1*3!+C_4^2*2!-C_4^3*1!+C_4^4\)

=24-24+12-4+1=9

对于合法方案,他只在“随意放”里面出现

对于x本书放在原来位置,其他错排的方案

例如1 2 4 3两本书放在了原来位置

随意放出现一次

”-规定一本书放在了原来位置,其他随意“出现\(C_x^1\)次

”+规定两本书放在了原来位置,其他随意“出现\(C_x^2\)次

由于\(\sum (-1)^i *C_x^i=0\),因此对答案贡献为0。

2:递推

\(D(n)=(n-1)*(D(n-1)+D(n-2))\)

如果n号元素放在了i位置,并且n位置放了i号元素

先选元素i,然后发现方案数为\(D(n-2)\)

结果是\((n-1)*D(n-2)\)

如果n号元素放在了i位置,并且n位置没有放i号元素

先选i,然后发现方案数为\(D(n-1)\)

结果是\((n-1)*D(n-1)\)
两个加起来用乘法分配律合起来即为

\(D(n)=(n-1)*(D(n-1)+D(n-2))\)

卡特兰数

定义

记\(C(n)\)为卡特兰数的第\(n\)项

\(C(n)代表在平面直角坐标系中只能走x+1或y+1,从(0,0)走到(n,n)且x \geq y的方案数\)

通项公式

\[C(n)=C_{2n}^n-C_{2n}^{n+1}={C_{2n}^n\over n+1} \]

证明:

图片帮助理解

首先根据组合数学,我们知道如果不考虑第一象限平分线的限制,方案数为\(C_{2n}^n\)在\(2n\)步中选\(n\)步走\(x\)

任何一种走到\((n,n)\)的非法方案,都至少有一个点\(p\)碰到了直线\(y=x+1\),那么我们将这条路径在\(p的右边的路经做关于y=x+1直线对称\),就会得到的终点为\((n-1,n+1)\)一条路径,容易得到的是若一条路径可以到达\((n-1,n+1)\)则必定会与\(y=x+1\)相交至少一次,对于任意一个交点\(p\)将这条路径\(p\)右边部分沿\(y=x+1\)翻折必定可以到达\((n,n)\)且一定不合法,到\((n-1,n+1)\)的方案数为\(C_{2n}^{n-1}(在2n步中选n-1步走x)\),所以总合法方案为\(C_{2n}^n-C_{2n}^{n+1}\)

化简:

\[\begin{split} C(n)&=C_{2n}^n-C_{2n}^{n+1}\\ &={(2n)!\over n!n!}-{(2n)!\over(n+1)!(n-1)!}\\ &={1\over n+1}({(2n)!(n+1)\over n!n!}-{(2n)!\over n!(n-1)!})\\ &={1\over n+1}({(2n)!(n+1)\over n!n!}-{(2n)!*n\over n!n!})\\ &={1\over n+1}{(2n)!(n+1)-(2n)!*n\over n!n!}\\ &={1\over n+1}{(2n)!\over n!n!}\\ &={1\over n+1}C_{2n}^n \end{split} \]

变形

\[C(n)={1\over n+1}\sum_{i=0}^n{C_n^i}^2 \]

递推

\[C(n+1)=\sum_{i=0}^nC(i)C(n-i),(C(0)=1)\\ C(n+1)={2(2n+1)\over n+2}C(n),(C(0)=1) \]

卡特兰数的前几项:1,1,2,5,14,42,132.

二项式反演

若

\[f(n)=\sum_{k=0}^n C_{n}^{k}g(k), \]

则

\[g(n)=\sum_{k=0}^n (-1)^{n-k}C_n^{k}f(k). \]

原理为行内交错和为0即:\(\sum_{i=1}^n(-1)^{i+1}C_n^i=0\)

若

\[F(k)=\sum_{j=k}^n C_{j}^{k}G(j), \]

则

\[G(k)=\sum_{j=k}^n (-1)^{j-k}C_{j}^{k}F(j). \]

以下是证明:

若n=4

\(f[4]=C_4^4g[4]\)

\(f[3]=g[3]+4g[4]=C_3^3g[3]+C_4^3g[4]\)

\(f[2]=g[2]+3g[3]+6g[4]=C_2^2g[2]+C_3^2g[3]+C_4^2g[4]\)

\(f[1]=g[1]+2g[2]+3g[3]+4g[4]\)

\(f[0]=g[0]+g[1]+g[2]+g[3]+g[4]\)

\(g[4]=f[4]\)

\(g[3]=f[3]-4f[4]=C_3^3f[3]-C_4^3f[4]\)

​ \(f[4]\)在其中的系数为\(C_3^3C_4^3-C_4^3C_3^3=0\)

\(g[2]=f[2]-3f[3]+6f[4]=C_2^2f[2]-C_3^2f[3]+C_4^2f[4]\)

​ \(g[3]\)在其中的系数\(C_2^2C_3^2-C_3^2C_3^3=0\)

​ \(g[4]\)在其中的系数\(C_2^2C_4^2-C_3^2C_4^3+C_4^2C_4^4\)

\(g[1]=f[1]-2f[2]+3f[3]-4f[4]\)

\(g[0]=f[0]-f[1]+f[2]-f[3]+f[4]\)

所以\(g[x]\)在\(f[y]\)中的系数为\(T=\sum_{i=y}^xc(-1)^{i-x}C_i^yC_x^i\)

化简:

\[\begin{split} T&=\sum_{i=y}^x(-1)^{i-y}C_{x-y}^{i-y}i\\ &=C_x^y\sum_{i=y}^x(-1)^{i-y}C_{x-y}^{i-y}\\ &=C_x^y\sum_{i=0}^{x-y}(-1)^{i}C_{x-y}^{i} \end{split} \]

右边求和为行内交错和即为0和至少型原理一样的,所以\(T=0\)

用Lucas定理求组合数

当p比较小时使用主要注意maxx为mod-1等于mod会有0出现

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define se second
#define fi first
using namespace std;
const int N=1e5+10;
const int mod=10007;
const ll inf=0x3f3f3f3f3f3f3f3f;
ll maxx=mod-1;
ll n,m,k,ans,cnt,t;
ll a[N],b[N];
ll jie[N],ni[N];
ll read(){ll x;cin>>x;return x;}
struct zzz{
	ll x,y,v;
}e[N];
int cmp(zzz a,zzz b){
	return a.v<b.v;
}
ll ppow(ll n,ll m){ll s=1;while(m){if(m&1)s=s*n%mod;n=n*n%mod;m/=2;}return s;}
ll c(ll n,ll m){
	if(n<m||n<0||m<0)
		return 0;
	return jie[n]*ni[m]%mod*ni[n-m]%mod;
}
ll lu(ll n,ll m){
	if(n<0||m<0)return 0;
	if(m==0)return 1;
	if(n<mod&&m<mod)return c(n,m);
	return c(n%mod,m%mod)*lu(n/mod,m/mod)%mod;
}
int main(){
	jie[0]=1;
	for(int i=1;i<=maxx;i++)jie[i]=(jie[i-1]*i)%mod;
	ni[maxx]=ppow(jie[maxx],mod-2);
	for(int i=maxx;i;i--)ni[i-1]=(ni[i]*i)%mod;
	cin>>t;
  ll x,y;
  cin>>x>>y;
  cout<<lu(x,y)<<'\n';
}

快速幂

ll ppow(ll n,ll m)
{
	ll s=1;
	while(m)
	{
		if(m&1) s=s*n%mod;
		n=n*n%mod;
		m/=2;
	}
	return s;
}

或

ll qpow(ll a,ll n,ll &mod){
    if(n==0) return 1;
    else if(n&1) return (qpow(a,n-1,mod)%mod*a)%mod;
    else {
        ll temp=(qpow(a,(n>>1),mod)%mod);
        return (temp*temp)%mod;
    }
}
posted @ 2026-10-20 09:46  Build-xhz31  阅读(3)  评论(0)    收藏  举报