当前位置: 首页 > news >正文

数论之组合数

组合数1: 

预处理每一个组合数的大小

类似于dp,从a个苹果里面选b个出来:首先从a个苹果里面拿出来一个,这样就分成了两种,一种是包括这个拿出来的苹果的方案数,此时就只需要拿b-1个苹果。一种是不包括这种苹果的方案数。

也就是c[a][b] = c[a-1][b] + c[a-1][b-1]

#include<bits/stdc++.h>
using namespace std;
const int N = 2010,mod = 1e7+7;
int c[N][N];// 将2000个数都预处理得到其组合数
void Init(){for(int i = 0; i < N; i++){for(int j = 0; j <= i; j++){if(!j) c[i][j] = 1;//从i个里面选0个,只有1种选法那就是不选else c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;}}
}int main() {Init();int n; scanf("%d",&n);while(n--){int a,b; scanf("%d %d",&a,&b);printf("%d\n",c[a][b]);}return 0;
}

组合数2:

预处理过程中的一步:阶乘

求组合数的公式:

C_{a}^{b}\, = \frac{a!}{(a - b)!\, *\, b!}

注意: 

\frac{a}{b}\, \,mod\, \, \, p\, \, \, != \frac{a\, \, mod\, \,p }{b \, \, \, mod p}        所以要化成逆元(快速幂求逆元),把除法变成乘法

时间复杂度N\log N

fact[i]\, =\, i!\,\, \, mod\, \, p                infact[i] = (i!)^{-1} \, \, \, mod\, \, \, p 

C_{a}^{b}\, = \, fact[a] * infact[a - b] *infact[b]

 

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 100010,mod = 1e9 + 7;
int fact[N],infact[N];
int ksm(int a,int k,int p){int res = 1;while(k){if(k & 1) res = (ll) res * a % p;k >>= 1;a = (ll) a * a % p;}return res;
}
int main() {fact[0] = infact[0] = 1;//预处理公式中的阶乘for(int i = 1; i < N; i++){fact[i] = (ll)fact[i - 1] * i;infact[i] = (ll) infact[i - 1] * ksm(i, mod - 2, mod) % mod;}int n; cin >> n;while(n--){int a,b; cin >> a >> b;cout <<(ll) fact[a] * infact[a - b] % mod * infact[b] % mod <<'\n';}return 0;
}

组合数3:

卢卡斯定理:

结论:

 

 卢卡斯定理的递归:

时间复杂度大约是p*logN*logp 

计算组合数值的C函数:

 所以循环只需要执行b次即可,由于分母除法不好计算,就运用了逆元(快速幂求),和分子累乘即可

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int p;//快速幂计算逆元
int ksm(int a, int k) {int res = 1;while (k) {if (k & 1) res = (ll)res * a % p;k >>= 1;a = (ll)a * a % p;}return res;
}
//利用公式计算组合数
int C(int a, int b) {if (b > a) return 0;int res = 1;for (int i = 1, j = a; i <= b; i++, j--) {res = (ll)res * j % p;// 计算分子res = (ll)res * ksm(i, p - 2) % p;//计算分母的逆元}return res;
}
//卢卡斯定理
int lucas(ll a, ll b) {if (a < p && b < p) return C(a, b);return (ll)C(a % p, b % p) * lucas(a / p, b / p) % p;// 递归计算组合数模 p 的值
}int main() {int n;cin >> n;while (n--) {ll a, b;cin >> a >> b >> p;cout << lucas(a, b) << '\n';}return 0;
}

  组合数4:

从定义出发,来算组合数,不需要取模

先把组合数分解质因数->实现高精度乘法

1.获取质数列表Prime

为什么需要分解质因数? 

2. 计算质数在阶乘中的出现次数 

get 函数计算了 n! 中质数 p 出现的总次数。这是通过逐次计算 np 的倍数出现的次数、p^2 的倍数出现的次数、p^3 的倍数出现的次数等来完成的。公式如下:

 3. 计算组合数的质因数分解

组合数 C(a, b) 中各个质数 p 的幂次:每个质数 p 的幂次为 a!p 的次数减去 b!(a-b)!p 的次数。

4. 高精度乘法计算组合数

mul 函数将高精度的大整数 a 和整数 b 相乘,并将结果存储在一个 vector<int> 中,以支持处理大数运算。结果按位存储,便于后续进一步的乘法运算。

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 5010;
int prime[N],cnt;
int sum[N];//每一个质数出现的次数
bool st[N];//筛法获取小于等于 n 的所有质数
void get_prime(int n)
{for(int i = 2;i <= n; i++){if(!st[i])prime[cnt++] = i;for(int j = 0; prime[j] * i <= n; j++){st[prime[j] * i]=true;if(i % prime[j]==0)break;//==0每次漏}}}
//计算n的阶乘里面包含的p的个数
int get(int n, int p){int res = 0;while(n){res += n / p;n /= p;}return res;
}// 高精度乘法
vector<int> mul(vector<int> a,int b){vector<int> c;int t = 0;for(int i = 0; i < a.size(); i++){t += a[i]*b;c.push_back(t % 10);t /= 10;}while(t){c.push_back(t % 10);t /= 10;}return c;
}
int main() {int a, b; cin >> a >> b;get_prime(a);  // 获取所有小于等于 a 的质数for(int i = 0; i < cnt; i++) {int p = prime[i];sum[i] = get(a, p) - get(a - b, p) - get(b, p); // 计算组合数公式中每个质数的幂次}vector<int> res;res.push_back(1);  // 初始化结果为 1for(int i = 0; i < cnt; i++) {for(int j = 0; j < sum[i]; j++) {res = mul(res, prime[i]);  // 依次将所有质数的幂次相乘}}for(int i = res.size() - 1; i >= 0; i--) cout << res[i];return 0;
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • git cherry-pick 合并多个提交
  • Go 调用Rust函数
  • 浅谈线性表——链表
  • AI编程简介
  • 【第69课】Java安全JWT攻防Swagger自动化算法签名密匙Druid未授权
  • java-Mybatis框架
  • MFC程序设计(一) MFC框架
  • 23种设计模式详细知识点(软件设计师)
  • 【工控】线扫相机小结
  • Linux编程:使用 CSV 与 UnQLite 进行数据存储的比较分析
  • Java中‘==’ 和 equals()的区别
  • GeoScene Pro教程(001):软件功能产品介绍
  • Win11配置Pytorch深度学习环境(GPU版本)
  • 鸿蒙HarmonyOS实战:IPC与RPC设备内进程通信
  • 【ROS2】launch启动文件:基础
  • 网络传输文件的问题
  • 【Linux系统编程】快速查找errno错误码信息
  • 2017前端实习生面试总结
  • echarts的各种常用效果展示
  • LeetCode29.两数相除 JavaScript
  • leetcode388. Longest Absolute File Path
  • nginx 配置多 域名 + 多 https
  • python 装饰器(一)
  • windows-nginx-https-本地配置
  • 老板让我十分钟上手nx-admin
  • 码农张的Bug人生 - 初来乍到
  • 设计模式 开闭原则
  • 新手搭建网站的主要流程
  • ​【经验分享】微机原理、指令判断、判断指令是否正确判断指令是否正确​
  • ​DB-Engines 11月数据库排名:PostgreSQL坐稳同期涨幅榜冠军宝座
  • #QT项目实战(天气预报)
  • #快捷键# 大学四年我常用的软件快捷键大全,教你成为电脑高手!!
  • (17)Hive ——MR任务的map与reduce个数由什么决定?
  • (八)Flink Join 连接
  • (八)五种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB
  • (利用IDEA+Maven)定制属于自己的jar包
  • (六)c52学习之旅-独立按键
  • (南京观海微电子)——示波器使用介绍
  • (生成器)yield与(迭代器)generator
  • (万字长文)Spring的核心知识尽揽其中
  • (五)activiti-modeler 编辑器初步优化
  • (五)MySQL的备份及恢复
  • (转)Groupon前传:从10个月的失败作品修改,1个月找到成功
  • (转)为C# Windows服务添加安装程序
  • ./configure、make、make install 命令
  • .Net MVC4 上传大文件,并保存表单
  • .NET 给NuGet包添加Readme
  • .net 流——流的类型体系简单介绍
  • .NET/C# 阻止屏幕关闭,阻止系统进入睡眠状态
  • .net6解除文件上传限制。Multipart body length limit 16384 exceeded
  • .Net6使用WebSocket与前端进行通信
  • .NET命令行(CLI)常用命令
  • /proc/interrupts 和 /proc/stat 查看中断的情况
  • [ 网络通信基础 ]——网络的传输介质(双绞线,光纤,标准,线序)
  • [20140403]查询是否产生日志