组合数学+ybt题解

加法原理

乘法原理

排列数

n 个数中任取 m 个元素的排列的方案数,表示为 Anm=n!(nm)!

0!=1

全排列 Ann

组合数

n 个元素中取出 m 个元素的组合的个数,表示为 (nm)=Anmm!=n!m!(nm)!

如何理解呢?就是把取出来的排列方案数取缔为一个组合,对于取出来的排列的方案数有 m! 个,所以用全排列除以 m! 即可

特别的,规定 m>n 时,Anm=(nm)=0

二项式定理

(a+b)n=i=0n(ni)anibi

通过这个式子,我们可以求出展开式的系数,另外的,杨辉三角也遵循这个规律

证明

二项式定理表明,对于任何正整数n和任何实数ab,都有以下等式成立:

(a+b)n=k=0n(nk)ankbk

其中,(nk)是组合数,表示从n个不同元素中取出k个元素的组合数。
下面使用数学归纳法来证明二项式定理:
基础步骤(n=0):
n=0时,等式左边为(a+b)0=1,等式右边为(00)a00b0=1,显然两边相等,基础情况成立。
归纳假设:
假设当n=m时,二项式定理成立,即

(a+b)m=k=0m(mk)amkbk

归纳步骤:
我们需要证明当n=m+1时,二项式定理也成立。
考虑(a+b)m+1,可以将其写成(a+b)(a+b)m,根据归纳假设,我们有:

(a+b)m+1=(a+b)k=0m(mk)amkbk

展开右边的乘积,我们得到:

=ak=0m(mk)amkbk+bk=0m(mk)amkbk

将两个求和式合并,我们可以重新排列和组合项:

=k=0m(mk)am+1kbk+k=0m(mk)amkbk+1

注意到第二个求和式中的项可以重新索引,将k+1替换为k,得到:

=k=0m(mk)am+1kbk+k=1m+1(mk1)am+1kbk

将两个求和式合并,并注意到当k=0时,第二个求和式中没有对应项,当k=m+1时,第一个求和式中没有对应项,因此我们可以将两个求和式合并为一个从k=0k=m+1的求和式:

=k=0m+1((mk)+(mk1))am+1kbk

利用组合数的性质(mk)+(mk1)=(m+1k),我们有:

=k=0m+1(m+1k)am+1kbk

这正是我们要证明的n=m+1时的二项式定理的形式。因此,归纳步骤成立。
由基础步骤和归纳步骤,我们可以得出结论,二项式定理对所有正整数n都成立。

组合数的递推



为什么这个公式成立呢? 考虑一种dp思想,如果我选第n个数,那么我有 Cn1m1 种方案,若我不选n,那我有 Cn1m 种方案

贴图真好用(逃

组合数的性质


考虑一个n位的二进制数,能组成的数有 2n 种,于是 Cnr 就是n位中有r个1的数的个数

卢卡斯定理及证明

ps:一般 p[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段

然后考虑隔板法,答案就是 Cn1k1

然后这题需要用到高精,懒得写了

T5:

把这个图形恒着切成两个矩形,然后枚举多少辆车在第一个矩形中,然后统计答案数即可

T6:

首先我们先把相同的数看成连续的一块

然后利用隔板问题,枚举可以分成 i 块,方案数即为 Cn1i1

然后考虑在块里填数,方案数即为 2Cni,因为我们选出来这些数可以按照递增,也可以按照递减顺序排列

特殊情况: i=1 时,因为只有一种数,所以不存在递增递减,所以要特判

ps:预处理逆元可以将复杂度降到 O(n),详见代码

代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+5,mod=2e6+3;
int n,ans;
int fac[N],inv[N],lg[50];
int quickpow(int x,int k){
    lg[0]=x;
    for(int i=1;i<=30;i++){
        lg[i]=lg[i-1]*lg[i-1]%mod;
    }
    int res=1;
    for(int i=0;i<=30;i++){
        if(!((k>>i)&1ll))  continue;
        res=lg[i]*res%mod;
    }
    return res;
}
int C(int b,int a){
    return fac[a]*inv[b]%mod*inv[a-b]%mod;
}
signed main(){
    scanf("%lld\n",&n);
    fac[0]=1;
    for(int i=1;i<=n;i++){
        fac[i]=fac[i-1]*i%mod;
    }
    inv[n]=quickpow(fac[n],mod-2);
    for(int i=n-1;i>=0;i--){
        inv[i]=inv[i+1]*(i+1)%mod;
    }
    ans=n;
    for(int i=2;i<=n;i++){
        ans+=2*C(i,n)%mod*C(i-1,n-1)%mod;
        ans%=mod;
    }
    printf("%lld\n",ans);
    return 0;
}

T3:

chat_GLM真好用(逃

然后

posted @   daydreamer_zcxnb  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
点击右上角即可分享
微信分享提示