加法原理
乘法原理
排列数
从 \(n\) 个数中任取 \(m\) 个元素的排列的方案数,表示为 \(A^m_n=\frac{n!}{(n-m)!}\)
\(0!=1\)
全排列 \(A^n_n\)
组合数
从 \(n\) 个元素中取出 \(m\) 个元素的组合的个数,表示为 \(\dbinom{n}{m}= \frac{A^m_n}{m!}=\frac{n!}{m!(n-m)!}\)
如何理解呢?就是把取出来的排列方案数取缔为一个组合,对于取出来的排列的方案数有 \(m!\) 个,所以用全排列除以 \(m!\) 即可
特别的,规定 \(m>n\) 时,\(A^m_n=\dbinom{n}{m}=0\)
二项式定理
\((a+b)^n=\sum^n_{i=0}\dbinom{n}{i} a^{n-i}b^i\)
通过这个式子,我们可以求出展开式的系数,另外的,杨辉三角也遵循这个规律
证明
二项式定理表明,对于任何正整数\(n\)和任何实数\(a\)和\(b\),都有以下等式成立:
其中,\(\binom{n}{k}\)是组合数,表示从\(n\)个不同元素中取出\(k\)个元素的组合数。
下面使用数学归纳法来证明二项式定理:
基础步骤(n=0):
当\(n=0\)时,等式左边为\((a + b)^0 = 1\),等式右边为\(\binom{0}{0} a^{0-0} b^0 = 1\),显然两边相等,基础情况成立。
归纳假设:
假设当\(n=m\)时,二项式定理成立,即
归纳步骤:
我们需要证明当\(n=m+1\)时,二项式定理也成立。
考虑\((a + b)^{m+1}\),可以将其写成\((a + b)(a + b)^m\),根据归纳假设,我们有:
展开右边的乘积,我们得到:
将两个求和式合并,我们可以重新排列和组合项:
注意到第二个求和式中的项可以重新索引,将\(k+1\)替换为\(k\),得到:
将两个求和式合并,并注意到当\(k=0\)时,第二个求和式中没有对应项,当\(k=m+1\)时,第一个求和式中没有对应项,因此我们可以将两个求和式合并为一个从\(k=0\)到\(k=m+1\)的求和式:
利用组合数的性质\(\binom{m}{k} + \binom{m}{k-1} = \binom{m+1}{k}\),我们有:
这正是我们要证明的\(n=m+1\)时的二项式定理的形式。因此,归纳步骤成立。
由基础步骤和归纳步骤,我们可以得出结论,二项式定理对所有正整数\(n\)都成立。
组合数的递推
为什么这个公式成立呢? 考虑一种dp思想,如果我选第n个数,那么我有 \(C^{m-1}_{n-1}\) 种方案,若我不选n,那我有 \(C^m_{n-1}\) 种方案
贴图真好用(逃
组合数的性质
考虑一个n位的二进制数,能组成的数有 \(2^n\) 种,于是 \(C^r_n\) 就是n位中有r个1的数的个数
卢卡斯定理及证明
ps:一般 \(p\in [1e5,1e6]\) ,适合用卢卡斯求组合数
T1:
递推出阶乘,再用快速幂算乘法逆元和次方即可
T2:
卢卡斯定理板子题
我们现预处理出模数以内的阶乘,然后用卢卡斯定理递归到模数以内后求解
代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e4,p=10007;
int t,n,m;
int fac[N],lg[60];
int quickpow(int x,int k){lg[0]=x;for(int i=1;i<=30;i++){lg[i]=lg[i-1]*lg[i-1]%p;}int res=1;for(int i=0;i<=30;i++){if(!((k>>i)&1)) continue;res=res*lg[i]%p;}return res;
}
int C(int n,int m){if(n<m) return 0;if(m==0) return 1;return fac[n]*quickpow(fac[m],p-2)%p*quickpow(fac[n-m],p-2)%p;
}
int lucas(int n,int m){if(n<m) return 0;if(m==0) return 1;return C(n%p,m%p)*lucas(n/p,m/p)%p;
}
signed main(){fac[0]=1;for(int i=1;i<=p;i++){fac[i]=fac[i-1]*i%p;}scanf("%lld",&t);while(t--){scanf("%lld%lld",&n,&m);printf("%lld\n",lucas(n,m));}return 0;
}
T3:
数论大杂烩!
还是很好理解的题解
代码:
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e4,mod=999911659;
int n,g;
int sum[5],lg[60],fac[5][N];
int p[6]={0,2,3,4679,35617,999911659};
int quickpow(int x,int k,int md){lg[0]=x;for(int i=1;i<=40;i++){lg[i]=lg[i-1]*lg[i-1]%p[md];}int res=1;for(int i=0;i<=40;i++){if(!((k>>i)&1ll)) continue;res=res*lg[i]%p[md];}return res;
}
int fan(int x,int y,int md){return quickpow(x,y-2,md);
}
int C(int n,int m,int md){if(n<m) return 0;if(m==0) return 1;return fac[md][n]*fan(fac[md][m],p[md],md)%p[md]*fan(fac[md][n-m],p[md],md)%p[md];
}
int lucas(int n,int m,int md){if(n<m) return 0;if(m==0) return 1;return C(n%p[md],m%p[md],md)*lucas(n/p[md],m/p[md],md)%p[md];
}
signed main(){for(int j=1;j<=4;j++){fac[j][0]=1;for(int i=1;i<=p[j];i++){fac[j][i]=fac[j][i-1]*i%p[j];}}scanf("%lld%lld",&n,&g);if(g%mod==0){printf("0\n");return 0;}for(int i=1;i*i<=n;i++){if(n%i!=0) continue;for(int j=1;j<=4;j++){sum[j]+=lucas(n,i,j);}if(i*i==n) continue;for(int j=1;j<=4;j++){sum[j]+=lucas(n,n/i,j);}}int ans=0;for(int i=1;i<=4;i++){int k=(mod-1)/p[i];int c=quickpow(k,p[i]-2,i);ans+=sum[i]*k*c;ans%=mod-1;}printf("%lld",quickpow(g,ans,5));
}
T4:
实际意义:有n个带编号的球,划分成标号连续的k段
然后考虑隔板法,答案就是 \(C^{k-1}_{n-1}\)