数论之组合数

news2024/11/14 6:17:23

组合数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);  // 初始化结果为 1
   for(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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2073949.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

嵌入式笔记:半加器与全加器

一&#xff0c;门电路 本文使用digital软件中的双掷继电器来实现以下的门电路&#xff0c;并结合这些门电路实现半加器与八位全加器。 与门 当输入信号A&#xff0c;B都置高电平时&#xff0c;继电器带电具有磁性&#xff0c;将下方双刀开关吸附&#xff0c;使电路导通。在输出…

C++20中的约束与概念

类模板、函数模板和非模板函数(通常是类模板的成员)可能与约束(constraint)相关联&#xff0c;该约束指定对模板参数的要求(requirements)&#xff0c;可用于选择最合适的函数重载和模板特化。约束是使用模板时需要通过模板参数满足的条件或要求。这些要求的命名集合称为概念(c…

Ai+若依(系统接口--Swagger):04篇

Swagger&#xff0c;能够自动生成 API 的同步在线文档&#xff0c;并提供Web界面进行接口调用和测试。 可以直接去测试&#xff1a;--有的接口测试需要权限 我们可以去这样操作 F12 报错404 是因为多了个前缀 /dev-api 我们去后台删掉&#xff1a; 重启刷新&#xff1a;

day 39 代码随想录 | 打家劫舍 动态规划

198.打家劫舍 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&#xff0c;如果两间相邻的房屋在同一晚上被小偷闯入&#xff0c;系统会自动报警。 给定一个代表每个…

Ruby+Watir进行web UI自动化测试

1.新建工程文件 打开RubyMine&#xff0c;新建一个工程文件目录如下&#xff1a; login_mail.rb文件 # encoding:UTF-8 # frozen_string_literal: true当(/^打开谷歌浏览器&#xff0c;进入163邮箱登陆页面$/) do$driver Watir::Browser.new :chromesleep(2)$driver.window.…

裸机:串口通信

串口通信的基本原理 单工通信和双工通信 (1)单工就是单方向&#xff0c;双工就是双方同时收发&#xff0c;同时只能但方向但是方向可以改变叫半双工 (2)如果只能A发B收则单工&#xff0c;A发B收或者B发A收&#xff08;两个方向不能同时&#xff09;叫半双工&#xff0c;A发B收…

【C++】类与对象篇一

【C】类与对象篇一 一 .面向过程和面向对象初步认识二 .类的详解1.类的引入2.类的定义3.类的访问限定符及封装&#xff08;面试题&#xff09;4.类的作用域5.类的实例化6.类对象模型 三 . 结构体内存对齐规则&#xff08;面试题&#xff09;四 . this指针1.this指针的特性2.thi…

WOFOST的web应用

目录 运行可视化 参考链接 https://github.com/irripro/WOFOST_streamlit/tree/main 运行 运行主程序.py之后&#xff0c;得到一串命令 将该命令放到命令台中运行 然后会转到WOFOST的web端上 可视化 目前好像只支持欧洲区域的模拟运行

C++篇:C向C++迈进(上)

引言 C语言作为编程基石&#xff0c;其高效与直接性深受开发者喜爱。然而&#xff0c;随着软件复杂度的增加&#xff0c;C以其面向对象及高级特性成为了新的选择。我们接下来将学习C&#xff0c;从C语言迈向C。 什么是C C 是一种高级语言&#xff0c;由 Bjarne Stroustrup 于…

Docker 的简介

Docker 的简介 为什么会有 Docker环境一致性问题提高资源利用率和可移植性快速部署和伸缩简化管理和维护版本控制和回滚 Docker 的历史dotCloud 时代&#xff08;2010年前&#xff09;Docker 诞生&#xff08;2010-2013&#xff09;快速发展与开源&#xff08;2013-2014&#x…

mysql 日期字段自动填写日期 及自动更新日期

INSERT 时 自动给日期字段 添加 当前日期时间&#xff1a; 在默认里选中&#xff1a; CURRENT_TIMESTAMP UPDATE 时 自动给日期字段 更新 当前日期时间&#xff1a; 勾选&#xff1a;根据当前时间戳更新

通过VIN车架号查询车辆登记日期

我们先来介绍下什么是vin码&#xff0c;以及vin码的构成结构解析&#xff0c;汽车VIN码&#xff0c;也叫车辆识别号码&#xff0c;通俗可以理解为汽车的身份证号码。 接口介绍 通过17位vin码&#xff0c;获取到车辆的发动机号&#xff0c;初登日期&#xff0c;车辆型号等信息。…

CI/CD之Jenkins用于Linux系统的部署方式汇总

目录 一、前言 二、CI/CD的定义与核心原则 CI/CD在现代软件开发中的重要性 CI/CD与Jenkins的关系 三、Jenkins部署方式汇总 1. 独立服务器部署 &#xff08;1&#xff09;离线安装 &#xff08;2&#xff09;在线安装 2. Docker容器部署 3. Kubernetes集群部署 4. 云…

聚焦自闭儿童:关注与理解

在探讨一个三岁自闭症孩子不会说话的情况时&#xff0c;我们首先需要理解自闭症这一复杂神经发育障碍的本质&#xff0c;以及它如何影响儿童的社交互动、沟通能力和行为模式。自闭症不仅仅是一个简单的“不会说话”的问题&#xff0c;而是一系列症状的综合体现&#xff0c;这些…

机器学习:随机森林决策树学习算法及代码实现

1、概念 随机森林&#xff08;Random Forest&#xff09;是一种集成学习方法&#xff0c;它通过构建多个决策树来进行分类或回归预测。随机森林的核心原理是“集思广益”&#xff0c;即通过组合多个弱学习器&#xff08;决策树&#xff09;的预测结果来提高整体模型的准确性和健…

基于Java的小区物业管理系统APP的设计与实现(论文+源码)_kaic

摘 要 小区物业管理系统是现代社会中非常热门的软件&#xff0c;伴随着社区规模的不断扩大和住户的不断增多&#xff0c;本系统的主要目的是辞别帐本以及传统的单一数据管理系统&#xff0c;快捷的保存用户各种数据信息。本系统针对Java系统展开&#xff0c;使用Java、SpringB…

tomcat服务器相关搭建

文章目录 web应用服务器tomcatTomcat功能及介绍配置tomcat服务器生成tomcat启动文件 nginx反向代理session共享服务器 web应用服务器tomcat Tomcat功能及介绍 Tomcat 服务器是一个免费的开放源代码的Web 应用服务器&#xff0c;属于轻量级应用服务器&#xff0c;在中小型系统和…

香橙派AIPro开发板安装PyQt5 aarch64

香橙派AIPro开发板安装PyQt5 参考 开发板信息 官网开发板信息 软件环境信息 1、安装依赖环境 sudo apt-get install cmake gcc g pip3 install --upgrade pip pip3 install wheel setuptools sudo apt-update sudo apt-get install qt5-default sudo apt-get install qtd…

为什么互联网上要设立防火墙?WAF又是什么?

防火墙&#xff08;英语&#xff1a;Firewall&#xff09;技术是通过有机结合各类用于安全管理与筛选的软件和硬件设备&#xff0c;帮助计算机网络于其内、外网之间构建一道相对隔绝的保护屏障&#xff0c;以保护用户资料与信息安全性的一种技术。 防火墙技术的功能主要在于及…

python库(21):

1 TextBlob简介 TextBlob 是一个基于 Python 的文本处理库&#xff0c;能够让基础的自然语言处理任务变得异常简单。 它提供了一个简单直观的 API&#xff0c;让你能够轻松执行词性标注、名词短语提取、情感分析、文本分类和关键词提取等功能。 值得一提的是&#xff0c;Tex…