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

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

目录

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

递推 杨辉三角

在这里插入图片描述
可以将 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;
}

如有不足,后续补充~

http://www.xdnf.cn/news/4474.html

相关文章:

  • AAAI2025论文整理-数字人驱动方向
  • spark 的流量统计案例
  • android-ndk开发(8): ndk 和 clang 版本对照表
  • 北京华锐视点邀您参与2025数字显示与元宇宙博览会【5月10-12日】
  • 浅谈Vue2 与 Vue3 的区别
  • 前端流行框架Vue3教程:13. 组件传递数据_Props
  • 学习Linux的第三天
  • 某振动分析系统的参数交叉核算
  • 解决 pnpm dev 运行报错的坎坷历程
  • 【第25节 性能指标计算】
  • 4.1框架应用
  • 系统架构师2025年论文《信息系统安全体系设计》
  • Xilinx DSP48E2 slice 一个周期能做几次float32浮点数乘法或者加法?如果是fix 32定点数呢?
  • “wsl --install -d Ubuntu-22.04”下载慢,中国地区离线安装 Ubuntu 22.04 WSL方法(亲测2025年5月6日)
  • python + whisper 读取蓝牙耳机, 转为文字
  • JavaScript 到命令和控制 (C2) 服务器恶意软件分析及防御
  • 三生原理是如何与狄利克雷定理兼容的?
  • 使用docker配置Mysql
  • 2021-10-29 C++被17或13整除最大10个数的和
  • 六六大顺--高精度+数学
  • 【QT】QT软件编译生成exe后,需要拷贝依赖库使用方法
  • 使用Windows+Linux实现mysql的主从复制
  • 【容器化】Docker容器技术入门基础教程
  • 【第四章】23-常见问题的快速处理
  • UKCC(原OUCC)真题讲解(一)
  • 代码随想录算法训练营总结篇
  • C++ 的 Tag Dispatching 技术
  • 人工智能 计算智能领域中分布估计算法的核心思想
  • 深度学习模型GoogLeNet的创新
  • 深入解析代理服务器:原理、应用与实战配置指南