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

求组合数【递推+快速幂+卢卡斯+线性筛】

目录

  • 递推 杨辉三角
  • 快速幂+逆元
  • 卢卡斯定理
  • 高精度 线性筛
  • 题目练习

递推 杨辉三角

在这里插入图片描述
可以将 C n m C_n^m Cnm理解为从n个不同的数中选m个有几种选法。
方案数就是 C n m C_n^m Cnm
由于n,m的范围不大,可以利用递归
递归式 C n m C_n^m Cnm= C n − 1 m C_{n-1}^m Cn1m+ C n − 1 m − 1 C_{n-1}^{m-1} Cn1m1
理解:
对第一个数有不选两种方式

  • 不选,从剩下的n-1个数中选m个 C n − 1 m C_{n-1}^m Cn1m
  • 选,从剩下的n-1个数中选m-1个 C n − 1 m − 1 C_{n-1}^{m-1} Cn1m1

特别的,选0次时应赋值为1,以便后面递推;
时间复杂度O(n*n)

void getC()
{for(int i=0;i<N;i++){for(int j=0;j<i;j++){if(j==0)c[i][j]=1;elsec[i][j]=c[i-1][j]+c[i-1][j-1];}}
}

在这里插入图片描述

通过列表发现,与杨辉三角的规律一致。

快速幂+逆元

在这里插入图片描述
当n,m的范围较大时,我们就不能用递归了,因为会TLE.
考虑用阶乘公式来计算:
在这里插入图片描述
阶乘的逆元怎么算?
在这里插入图片描述
为了能清楚的理解,还是建议大家自己推导推导~
在这里插入图片描述

时间复杂度O(nlogP)

void qmi(int a,int b)//快速幂
{int ans=1;while(b){if(b&1)ans=ans*a%P;a=a*a%P;b>>=1; }return ans;
}
void init()//打表 
{f[0]=g[0]=1;for(int i=1;i<N;i++){f[i]=f[i-1]*i%P;g[i]=g[i-1]*qmi(i,P-2)%P;} 
}
int getC()
{return f[n]*g[m]%P*g[n-m]%P;
}

卢卡斯定理

在这里插入图片描述
这个n和m的范围太大了,上面两个方法都不行,就要学习新的方法啦。
这个新的方法是让m/p和n/p的组合数与m%p和n%p的组合数相乘

在这里插入图片描述
时间复杂度O(plogp+logpn)
代码很简单,会用到上面的内容

void qmi(int a,int b)
{int ans=1;while(b){if(b&1)ans=ans*a%P;a=a*a%P;b>>=1; }return ans;
}
void init()//打表 
{f[0]=g[0]=1;for(int i=1;i<N;i++){f[i]=f[i-1]*i%P;g[i]=g[i-1]*qmi(i,P-2)%P;} 
}
int getC()
{return f[n]*g[m]*g[n-m]%p;
}
int lucas(int n,int m,int p)
{if(m==0)return 1;return lucas(n/p,m/p,p)*getC(n%p,m%p,p)%p;
}

高精度 线性筛

在这里插入图片描述

组合数的增长速度相当的快,必须用高精度来存。

  • 1.筛质数,把1~n之间的质数筛出来。
  • 2.枚举所有质数。
    在这里插入图片描述
    代码实现:
int get(int n,int p)//n的阶乘中p的个数 
{int s=0;while(n)s+=n/p,n/=p;return s;
}
int getC(int n,int m,int p)//C中P的个数 
{return get(n,p)-get(m,p)-get(n-m,p);
}
void mul(int c[],int p,int &len)//高精度 
{int t;for(int i=0;i<len;i++){t+=c[i]*p;c[i]=t%10;t/=10;} while(t){c[len++]=t%10;t/=10;}
}
int main()
{int c[N],len=1,c[0]=1;for(int i=0;i<cnt;i++){int p=prim[i];int s=getC(n,m,p);while(s--)mul(c,p,len);}
}

题目练习

题目来源:B3717 组合数问题

题目描述

给出 T T T 次询问,每次给出 n , m n,m n,m,请求出 ( n m ) \binom{n}{m} (mn) 998 , 244 , 353 998,244,353 998,244,353 取模的结果。

其中 ( n m ) \binom{n}{m} (mn) 为二项式系数,它的另一种写法是 C n m C_n^m Cnm

输入格式

输入的第一行是两个整数,分别表示询问的次数 T T T 和所给出 n n n 的最大值 N N N
接下来 T T T 行,每行两个整数,依次表示给出的 n n n m m m

输出格式

为了避免输出过大,请你输出一行一个整数,表示所有询问的结果的按位异或和

输入 #1

3 5
3 3
4 2
5 3

输出 #1

13

说明/提示

样例 1 解释

三组询问的答案依次是 1 , 6 , 10 1, 6, 10 1,6,10

数据规模与约定

100 % 100\% 100% 的数据,保证 1 ≤ T ≤ 5 × 1 0 6 1 \leq T \leq 5 \times 10^6 1T5×106 0 ≤ m ≤ n ≤ N ≤ 5 × 1 0 6 0 \leq m \leq n \leq N \leq 5 \times 10^6 0mnN5×106

提示

请注意大量的数据读入对程序效率造成的影响,选择合适的读入方式,避免超时。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 5e6+5;
int g[N], f[N], q[N];
const int p = 998244353;
void init(int n, int p) {g[1] = 1, f[0] = 1, q[0] = 1;for(int i = 2; i <= n; i++) g[i] =  (p - p / i) * g[p % i] % p; // 线性求逆元for(int i = 1; i <= n; i++) f[i] =  f[i - 1] * g[i] % p, q[i] =  q[i - 1] * i % p; // 线性求阶乘逆元
}
signed main() {ios::sync_with_stdio(0);int ans = 0, t, maxn;cin >> t >> maxn;init(maxn, p);while(t--) {int n, m; cin >> n >> m;int a =  q[n] * f[m] % p * f[n - m] % p;ans ^= a;}cout << ans << endl;return 0;
}

如有不足,后续补充~

相关文章:

  • 单例模式的实现方法
  • Android数据库全栈开发实战:Room+SQLCipher+Hilt企业级应用构建
  • 解决Centos连不上网
  • 自定义一个 Spring Boot Starter -笔记
  • 广州华锐视点邀您参与2025广交会VRAR展【5月10-12日】
  • 大数据产品销售数据分析:基于Python机器学习产品销售数据爬虫可视化分析预测系统设计与实现
  • 20250506格式化NanoPi NEO开发板使用Ubuntu core16.04系统的TF启动卡
  • Spark 的 Shuffle 机制:原理与源码详解
  • 医疗健康软件专利:给生命科学装个 “智能防盗门“
  • vue项目中渲染markdown并处理报错
  • 电池热管理CFD解决方案,为新能源汽车筑安全防线
  • 汽车紧固件防腐3.0时代:敦普水性漆用无铬锌铝涂层定义「零氢脆」标准
  • 人工智能与生命科学的深度融合:破解生物医学难题,引领未来科技革命
  • Qt—鼠标移动事件的趣味小程序:会移动的按钮
  • 2025最新vmware-17虚拟机安装教程(保姆级,图文讲解,带安装包)
  • MySQL基础关键_009_DDL 和 DML(二)
  • 多线程2-多线程编程
  • 【Fifty Project - D23】
  • 从入门到登峰-嵌入式Tracker定位算法全景之旅 Part 7 |TinyML 定位:深度模型在 MCU 上的部署
  • 扩增子分析|微生物生态网络稳定性评估之鲁棒性(Robustness)和易损性(Vulnerability)在R中实现
  • 创新创业50人论坛开幕在即,双创青年为何选择来上海筑梦?
  • “五一”假期国内出游3.14亿人次,同比增长6.4%
  • 山东滕州一车辆撞向公交站台致多人倒地,肇事者被控制,案件已移交刑警
  • 我给狗狗上课,月赚四五万
  • 武契奇目前健康状况稳定,短期内将暂停日常工作
  • 胖东来回应“浙江‘胖都来’卖场开业”:已取证并邮寄律师函