组合数学基础
分类加法原理
有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的幂次为\)(用建房子的思路):
然后\(C_{n+m}^m含p的幂次为\):
接下来只需要算出\(n+m\)在p进制下是进位次数(加法过程中)就行。
将\(n+m,n,m\)分别写成p进制(0为最低位)
在加法过程中,定义第\(pos\)位发生进位的条件为:
若第\(pos\)位发生进位则
当然由于进位最多进一\((\lfloor{{(p-1)+(p+1)}\over{p}}\rfloor)\),故当第\(pos\)位发生进位时
反之
即
然后因为
在p进制下\(n+m\)过程中进位次数用式子表示就是
证明完毕
错排
表示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_{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}\)
化简:
变形
递推
卡特兰数的前几项:1,1,2,5,14,42,132.
二项式反演
若
则
原理为行内交错和为0即:\(\sum_{i=1}^n(-1)^{i+1}C_n^i=0\)
若
则
以下是证明:
若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\)
化简:
右边求和为行内交错和即为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;
}
}

浙公网安备 33010602011771号