从 中选择 个数的方案数,记为 ,也可记为 。读作 “ 取 ”。
引理:在杨辉三角中的第 行 列等于 。 根据这个引理,我们可以用构造杨辉三角的形式构造组合数。 杨辉三角的构造很简单,就是当前位等于上面的加左上的,也就是 ,转换成组合数就是 。
时间复杂度 。
注意点 1.注意边界 。
code
cpp#include<bits/stdc++.h>
using namespace std;
const int N=5050,mod=1e9+7;
int c[N][N];
void get_c(int n){
for(int i=0;i<=n;i++){
for(int j=0;j<=i;j++){
if(!j)c[i][j]=1;
else c[i][j]=c[i-1][j]+c[i-1][j-1];
c[i][j]%=mod;
}
}
}
int main(){
int n,m;
cin>>n>>m;
get_c(max(n,m));
cout<<c[n][m]%mod;
return 0;
}
展开 ,发现这个式子可以直接使用阶乘求解,但是答案一般都很大,除法又正好不满足同余定理,所以这时,要用逆元来解决。
由逆元的定义可得 ,所以只要预处理 到 的阶乘逆元,最终答案就是 。
时间复杂度 。 注意点 1.注意在求组合数时可以模,因为转换成了逆元。
code
cpp#include<bits/stdc++.h>
using namespace std;
const int N=5e6+10;
int n,m;
typedef unsigned long long ll;
const ll mod=998244353;
ll qpow(ll a,ll b,ll p){
ll res=1;
a%=p;
while(b){
if(b&1){
res=res*a%p;
}
a=a*a%p;
b>>=1;
}
return res;
}
int T,Q;
ll inv[N];
ll fact[N];
void init(int n){
fact[0]=1;
for(int i=1;i<=n;i++){
fact[i]=fact[i-1]*i%mod;
}
int k=qpow(fact[n],mod-2,mod);
inv[n]=k;
for(int i=n;i>=1;i--){
inv[i-1]=inv[i]*i%mod;
}
}
ll C(ll n,ll m){
return fact[n]*inv[m]%mod*inv[n-m]%mod;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>T>>Q;
init(Q);
ll res=0;
while(T--){
cin>>n>>m;
res^=C(n,m);
}
cout<<res;
return 0;
}
二项式定理
我们早就学过,杨辉三角的第 行就是 的展开式的各项系数,所以我们可以根据杨辉三角和组合数的关系,推出如下式子:
这个展开式首先就要把每一位加上,所以要用求和。 表示的是杨辉三角第 行的第 个数,也就是这一位的系数。因为 的每一项的指数和都是 ,且 的指数逐渐减少, 的指数是逐渐增大。注意可以 ,因为有 作为系数,省略。
