组合数的计算

article/2025/9/23 18:25:36

本篇博客来自南昌理工学院acm集训队成员yyj

组合数

1.定义

组合数:从 n 个不同元素中每次取出 m 个不同元素 ,不管其顺序合成一组,称为从 n 个元素中不重复地选取 m 个元素的一个组合。所有这样的组合的种数称为组合数。

2.性质与描述

2.1写法

在线性写法中被写作C(n,m)。

组合数的计算公式为:
在这里插入图片描述

2.2性质

性质1.

从n个不同元素中取出m个元素的组合数 == 从n个不同元素中取出 (n-m) 个元素的组合数;
理解:
这个性质很容易理解,例如C(9,2)=C(9,7),即从9个元素里选择2个元素的方法与从9个元素里选择7个元素的方法是相等的。
互补性质

性质2:

组合恒等式 :
若表示在 n 个物品中选取 m 个物品,则如存在下述公式:

C(n,m)=C(n,n-m)=C(n-1,m-1)+C(n-1,m)。

关于其数学证明可以看:这篇文章
dp证明:(帅哥美女必会)
如果学过一点点dp的帅哥美女都知道利用dp思想来解决问题
ex:

状态表示:
c[i][j]表示在i个物品里选j个的选法总数;那么选的状态:选这个物品 或者 不选 选第j个物品的总数为:c[i-1][j];
不选第j个物品的总数为:c[i-1][j-1];(就是从前j-1个物品里选)那么状态方程可以表示为:
c[i][j]=c[i-1][j-1]+c[i-1][j];

3.代码运用与解释:

3.1说明:

在学习算法的过程中注意数据范围十分重要的,重要到就像有多少钱取什么样的老婆,在算法过程中数据范围越大,你的钱越少,挑老婆的余地也越少,那么使用的方法也就越难。接下来作者本人就带你学习一下。

3.2四大方法(有多少钱就选什么样的老婆):

描述:
有a个物品,挑b个物品问有几种选法,询问n次。

(1):1<=b<=a<=2000,10万次询问。

解法:先预处理一下(递推),把所有答案都预处理出来,然后直接询问出答案。

处理方式:运用的组合恒等式 双重循环,dp求解。

代码如下:

// c[a][b] 表示从a个中选b个的方案数
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]) % mod;

时间复杂度为:O(a^2);

(2).1<=a<=b<=100000,1万次询问,答案太大 需要mod 1e9+7。

解法:利用逆元求解
简单了解一下逆元:设x为a的逆元,那么有x*a(mod p)== 1;(a要和p互质
x称为a模上p的逆元,那么x可以理解为a的倒数。因为乘起来结果取模是一样的。

逆元的求法:运用费马小定理
a与p互质那么:b^(p-1)mod§==1。
把b^(p-1)拆开来:b * b ^(p-2).
那么b ^(p-2)就相当于x了,即为b的逆元。

但是还是有很多细节需要注意,p可能太大了,需要快速幂来求解

在这里插入图片描述

看代码:

#include<iostream>
using namespace std;
const int N=1e5+10,mod=1e9+7;;
long long f[N],nf[N];
int ksm(int a,int b,int p)    //快速幂
{long long res=1;while(b){if(b&1)res=res*a%p;a=(long long)a*a%p;b>>=1;}return res;
}
int main()
{f[0]=nf[0]=1;                    //0!=1,f[i]为i的阶层,nf为逆元for(int i=1;i<=N;i++){f[i]=f[i-1]*i%mod;nf[i]=nf[i-1]*ksm(i,mod-2,mod)%mod;   //快速幂求逆元}int t;cin>>t;while(t--){int a,b;cin>>a>>b;printf("%lld\n",f[a]*nf[a-b]%mod*nf[b]%mod);}return 0;
}

(3).1<=b<=a<=1e^18,1<=p<=1e5;

在此引入卢卡斯定理:
若p是质数,则对于任意整数 1 <= m <= n,有:
C(n, m) = C(n % p, m % p) * C(n / p, m / p) (mod p)

#include<iostream>
using namespace std;long long ksm(long long a,long long b,long long p)
{long long res=1;while(b){if(b&1)res=res*a%p;a=a*a%p;b>>=1;}return res;
}
long long C(long long a,long long  b,long long p)
{long long res=1;for(int i=1,j=a;i<=b;i++,j--)      {res=res*j%p;res=res*ksm(i,p-2,p)%p;       //至于为什么,自己想。或者是看下面的解释}return res;
}long long lucas(long long a,long long b,long long p)
{if(a<p&&b<p)return C(a,b,p);      //直接returnelse return C(a%p,b%p,p)*lucas(a/p,b/p,p)%p;   //因为a/p不一定比p小,所以还是需要调用一次
}
int main()
{int t;cin>>t;while(t--){long long  a,b,p;cin>>a>>b>>p;cout<<lucas(a,b,p)<<endl;}
}

为什么可以这样求解 :
Cba=a!(a−b)!∗b!=a∗(a−1)∗(a−2)∗…∗(a−b+1)∗(a−b)∗…∗1/(a−b)∗(a−b−1)∗…∗1∗b!
=a∗(a−1)∗(a−2)∗…(a−b+1)/b!
分子一共有a-(a-b)项,也就是b项。所以直接分别求阶层即可。

(4).1<=a<=b<=5000,不取模

当我们需要求出组合数的真实值,而非对某个数的余数时,分解质因数的方式比较好用:
1. 筛法求出范围内的所有质数
2. 通过 C(a, b) = a! / b! / (a - b)! 这个公式求出每个质因子的次数。 n! 中p的次数是 n / p + n / p^2 + n / p^3 + …
3. 用高精度乘法将所有质因子相乘

#include<iostream>
using namespace std;
const int  N=5010;
bool str[N];
int prime[N],c[N],res[N],x;void muli(int b)     //高精度
{int t=0;for(int i=1;i<=x;i++){res[i]=res[i]*b+t;t=res[i]/10;res[i]=res[i]%10;}while(t){res[++x]=t%10;t=t/10;}while(res[x]==0&&x>0)x--;
}
int get(int a,int p)    //求质因子的个数a!中质因子为p的个数
{int t=0;while(a){t+=a/p;a/=p;}return t;
}
int main()
{int a,b;cin>>a>>b;for(int i=2;i<=a;i++){if(str[i]==0)for(int j=i+i;j<=a;j+=i)str[j]=true;     筛质数}for(int i=2;i<=a;i++){if(!str[i]){c[i]=get(a,i);}}for(int i=2;i<=b;i++){if(!str[i]){c[i]-=get(b,i);       //  减去b!的质因子}}for(int i=2;i<=a-b;i++){if(!str[i]){c[i]-=get(a-b,i);            //  减去(a-b)!的质因子}}res[1]=1;x=1;    //x为位数for(int i=2;i<=a;i++){for(int j=1;j<=c[i];j++){muli(i);}}for(int i=x;i>=1;i--)printf("%d",res[i]);return 0;
}

作者有话说:
基础打得好学到后面你自然学的就快,切莫狂妄自大,学习组合数运用到了很多知识点:
快速幂,高精度,筛法筛质数,逆元,费马小定理,卢卡斯定理,dp原理,只有拥有砸肆的基础才能走得更高。

话不多说,认真学习的帅哥有奖励:给你们看看漂亮小姐姐:
请添加图片描述


http://chatgpt.dhexx.cn/article/PIJc5Rvd.shtml

相关文章

组合数的性质证明

性质 极其简单的证明 根据杨辉三角的性质&#xff0c;第x行第y个的值为C(x-1,y-1) 那么 一目了然。 严肃的证明

组合数性质--二项式系数之和等于2^n的证明

1.公式 首先我们都知道组合数的意义&#xff0c;就是说一共有n个样本&#xff0c;一次性从中取出m个样本&#xff0c;一共有多少种不同的取法。它的公式如下&#xff1a; 它有这么一个性质&#xff1a; 该性质有若干种证明方式&#xff0c;今天我在这边写出我觉得挺巧妙的一种…

组合数原理

资料来源&#xff1a;https://baike.baidu.com/ 组合数 从n个不同元素中&#xff0c;任取m(m≤n)个元素并成一组&#xff0c;叫做从n个不同元素中取出m个元素的一个组合&#xff1b;从n个不同元素中取出m(m≤n)个元素的所有组合的个数&#xff0c;叫做从n个不同元素中取出m个…

深入详细理解矩阵 (矩阵的加减乘、转置、共轭、共轭转置)

简介 矩阵:英文名Matrix。在数学名词中&#xff0c;矩阵用来表示统计数据等方面的各种有关联的数据。这个定义很好地解释了Matrix代码制造世界的数学逻辑基础。矩阵是数学中最重要的基本概念之一&#xff0c;是代数学的一个主要研究对象&#xff0c;也是数学研究及应用的一个重…

【线性代数之二】矩阵与行列式

一、矩阵 1.1 定义 由 m n 个数aij排成的m行n列的数表称为m行n列的矩阵&#xff0c;简称m n矩阵。记作&#xff1a; 这mn 个数称为矩阵A的元素&#xff0c;简称为元&#xff0c;数aij位于矩阵A的第i行第j列&#xff0c;称为矩阵A的(i,j)元&#xff0c;以数 aij为(i,j)元的…

CString类型转换为LPCSTR类型

今天编程遇到一个问题&#xff0c;就是openGL中某个函数需要传入LPCSTR类型的参数&#xff0c;而通过MFC对话框获取得到的是CString类型的参数&#xff0c;因此需要将CString转化为LPCSTR类型&#xff0c;网上有很多这样的强转类型&#xff0c;然而却发现在强转的时候没有用&am…

linux jstat 简介

本文目录一览&#xff1a; 1、Linux使用jstat命令查看jvm的GC情况2、linux怎么监控 jvm内存 jstat3、Linux系统监控要用到哪些命令4、linux上如何安装jstatd服务 Linux使用jstat命令查看jvm的GC情况 Linux 使用jstat命令查看jvm的GC情况 命令格式 jstat命令命令格式&#…

内存屏障与volatile(C语言版)

内存屏障与volatile&#xff08;C语言版&#xff09; 最有价值的写在最前面 内存屏障与volatile是高并发编程中比较常用的两个技术&#xff0c;无锁队列的时候就会用到这两项技术。然而这两项技术设计比较广的基础知识&#xff0c;所以比较难以理解&#xff0c;也比较不容易解…

WDM驱动

WDM英文Windows Driver Model(WDM)的缩写。 简介 WDM WDM是WINDOWS2000认证的驱动程序&#xff0c;WIN2000由NT发展而来&#xff0c;所以对于设备的支持功能有限&#xff0c;同时为了最大限度的保障稳定性&#xff0c;所以推崇WDM驱动&#xff0c;但同时WDM驱动也就是功能最少…

快速转换dBm与W

dBm是一个表示功率绝对值的值&#xff08;也可以认为是以1mW功率为基准的一个比值&#xff09;&#xff0c;计算公式为&#xff1a;10log&#xff08;功率值/1mw&#xff09;。 这里我们介绍一种将dBm转换为W的口算方法&#xff0c;这一方法总结起来就是 “1个基准”和“2个原则…

学内核之十一:ARM64屏障指令使用指南

关于屏障指令&#xff0c;主要是与乱序有关。特别是在内核开发中&#xff0c;这是一个非常重要的主题。屏障由乱序引起&#xff0c;乱序则是由优化引起。 自从摩尔定律不断被逼近极限&#xff0c;半导体的优化不再单纯通过提升频率来实现。多核心、并发执行变成了主流的优化思路…

WTM

WTM的由来 WalkingTec.Mvvm框架&#xff08;简称WTM&#xff09;最早开发与2013年&#xff0c;基于Asp.net MVC3 和 最早的Entity Framework, 当初主要是为了解决公司内部开发效率低&#xff0c;代码风格不统一的问题。经历了四年间数十个项目的考验&#xff0c;框架逐步的完善…

WMIC使用

目录 WMI 远程创建进程 wmiexec wmiexec.py wmiexec.vbs Invoke-WmiCommand.ps1 Invoke-WMIMethod WMI WMI全称“windows管理规范”&#xff0c;是一个windows服务。从win2003开始一直存在。它原本的作用是方便管理员对windows主机进行管理。因此在内网渗透中&#xff…

Arm64内存屏障

一、内存类型 ARMv8架构将系统中所有的内存&#xff0c;按照它们的特性&#xff0c;划分成两种&#xff0c;即普通内存和设备内存。并且它们是互斥的&#xff0c;也就是说系统中的某段内存要么是普通内存&#xff0c;要么是设备内存&#xff0c;不能都是。 1&#xff09;普通…

barrier(wmb,mb,rmb)和cache coherence

http://www.linuxforum.net/forum/gshowflat.php?Cat&BoardlinuxK&Number428239&page5&viewcollapsed&sb5&oall&fpart 注: 这里的barrier 指的是wmb, rmb, mb. 一 直找不到合适的资料说明barrier和 Cache coherence 之间的关系. 在<<ldd>…

mw与dbm换算

1. 基本概念 dbm&#xff1a;意即分贝毫X&#xff0c;可以表示分贝毫伏&#xff0c;或者分贝毫瓦。他是一个表示功率绝对值的单位。 功率/电平&#xff08;dBm&#xff09;&#xff1a;放大器的输出能力&#xff0c;一般单位为w、mw、dBm。dBm是取1mw作基准值&#xff0c;以分贝…

[architecture]-DBG、DMB、DSB 和 ISB指令介绍

快速链接: . &#x1f449;&#x1f449;&#x1f449; 个人博客笔记导读目录(全部) &#x1f448;&#x1f448;&#x1f448; 付费专栏-付费课程 【购买须知】: 【精选】ARMv8/ARMv9架构入门到精通-[目录] &#x1f448;&#x1f448;&#x1f448; 1、DBG、DMB、DSB 和 IS…

WMB在项目中的应用

提纲&#xff1a; 1、 WebSphere Message Broker Introduction a) ESB Overview b) Message Broker Overview c) Message Broker Performance Report 2、 ESB Project Sharing 内容&#xff1a; 1、 Message Broker是建立在MQ基础之上的。【说明消息中间件对于MB是何等的重…