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

求组合数专题

求组合数 Ⅰ(递推公式)

思路 O(a\cdot b)

  • 递推法预处理
    • 利用公式 C_{a}^{b} = C_{a-1}^{b-1} + C_{a-1}^{b}
    • 复杂度 O(a \cdot b) = 4*10^{6}
  • 直接查询
    • 单次查询复杂度 O(1)

代码

#include <bits/stdc++.h>
using namespace std;
const int N = 2010;
const int mod = 1e9+7;
int c[N][N];
int get_c(int a, int b)
{c[0][0] = 1;for(int i = 1; i <= a; i++){c[i][0] = 1;for(int j = 1; j <= a; j++){c[i][j] = (c[i-1][j-1] + c[i-1][j]) % mod;}}return c[a][b];
}
int main()
{get_c(2000, 2000);int n;cin >> n;while (n -- ){int a, b;cin >> a >> b;cout << c[a][b] << '\n';}
}

求组合数 Ⅱ (乘法逆元、费马小定理、快速幂)

思路 O(1e5 \cdot log(p-2) + n)

  • 很明显,递推法预处理会超时。于是我们选择另一种计算组合数的方式:快速幂(处理分子的阶乘、分母的阶乘) + 费马小定理(将除以分母,在模运算中该换为,乘以分母的乘法逆元)
    • 步骤       
      • 计算阶乘 O(b_{max}) = 10^{5}
      • 快速幂求乘法逆元 O(log(p-2)) ,p = 1e9+7
    • 总复杂度 O(n \cdot (b_{max} + log(p-2))) > 10^{9}
    • 反思:不同的数据计算阶乘时重复计算了
    • 改进:预处理所有阶乘 1e5
      • (同时可以预处理所有阶乘的乘法逆元)O(1e5 * log(p-2))
    • 注意:求 \frac{a!}{(a-b)!} 时 仍不能用除法,要采用乘法逆元

代码

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
const int mod = 1e9+7;
typedef long long ll;
ll fac[N], ifac[N];
ll qmi(ll base, ll expo)
{ll retv = 1;while(expo){if(expo & 1) retv = retv * base % mod;base = base * base % mod;expo >>= 1;}return retv;
}
void init()
{fac[0] = ifac[0] = 1;for(ll i = 1; i <= 100000; i++){fac[i] = fac[i-1] * i % mod;ifac[i] = qmi(i, mod-2) * ifac[i-1] % mod;}
}
int main()
{init();int n;cin >> n;while (n -- ){int a, b;cin >> a >> b;cout << fac[a] * ifac[a-b] % mod * ifac[b] % mod << '\n';}
}

求组合数 Ⅲ(卢卡斯定理)

思路 

  • 无它,ab值太大了,幸好最后是mod运算,用卢卡斯定理即可 C_{a}^{b} = C_{a \mod p}^{b \mod p} \cdot C_{\frac{a}{p}}^{\frac{b}{p}}
    • 条件: p是质数

代码

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
typedef long long ll;
ll qmi(ll base, ll expo, ll mod)
{ll retv = 1;while(expo){if(expo & 1) retv = retv * base % mod;base = base * base % mod;expo >>= 1;}return retv;
}
ll get_c(ll a, ll b, ll mod)
{ll res = 1;for(int i = 1, j = a; i <= b; i++, j--){res = res * j % mod;res = res * qmi(i, mod-2, mod) % mod;}return res;
}
ll lucas(ll a, ll b, ll mod)
{if(a < mod && b < mod)return get_c(a,b,mod);else return get_c(a % mod, b % mod, mod) * lucas(a / mod, b / mod, mod) % mod;
}
int main()
{int n;cin >> n;while (n -- ){ll a, b, p;cin >> a >> b >> p;cout << lucas(a, b, p) << '\n';}return 0;
}

求组合数 Ⅳ(高精度乘法、质因数分解)

思路

  • 阶乘的质因数分解 + 高精度乘法
#include <bits/stdc++.h>
using namespace std;
const int N = 5010;
int primes[N], cnt;
bool st[N];
int s[N];
vector<int> v;
void get_primes(int n)
{for(int i = 2; i <= n; i++){if(!st[i]) primes[++cnt] = i;for(int j = 1; primes[j] * i <= n; j++){st[primes[j] * i] = true;if(i % primes[j] == 0) break;}}
}
int cal(int x, int p)
{int retv = 0;while(x){retv += x/p;x /= p;}return retv;
}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()
{get_primes(5000);int a, b;cin >> a >> b;v.push_back(1);for(int i = 1; i <= cnt; i++){int p = primes[i];s[p] = cal(a, p) - cal(a-b, p) - cal(b, p);for(int j = 1; j <= s[p]; j++)v = mul(v, p);}for(int i = v.size()-1; i >= 0; i--) cout << v[i];return 0;
}


http://www.mrgr.cn/news/40148.html

相关文章:

  • Win11禁止搜索栏查找互联网内容
  • 3.1K Star,这款开源在线视频下载神器绝了,速度达 30M/S
  • 【C语言】字符和字符串函数(2)
  • wine 中文显示成小方框
  • AD软件的分屏显示功能
  • zabbix7.0监控linux主机案例详解
  • Android 安装应用-提交阶段之后剩下的操作
  • 算法:153.寻找旋转排序数组中的最小值
  • Kd-tree介绍和使用
  • 室内定位论文整理-20240930期
  • python如何判断图片路径是否存在
  • DRF实操——项目部署
  • Conda 虚拟环境使用指南,python,anaconda,miniconda
  • 自己做个国庆75周年头像生成器
  • 解决 Could not locate zlibwapi.dll. Please make sure it is in your library path
  • LLM 构建Data Multi-Agents 赋能数据分析平台的实践之⑥:NL2SQL技术探讨
  • TI DSP TMS320F280025 Note16:EPWM的原理与使用
  • 粉丝们得以一窥索菲亚罗兰奢华的90岁生日庆祝仪式! 她已完成了所有的遗愿清单 !
  • 删除二叉树中以x为根节点的子树(包括根结点)
  • Java常用三类定时器快速入手指南