[数论第三节]高斯消元法/求组合数/卡特兰数

这篇具有很好参考价值的文章主要介绍了[数论第三节]高斯消元法/求组合数/卡特兰数。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

  • 高斯消元

    • 求解含有n个未知数,n个方程的多元线性方程组 O(n^3)
    • 初等行变换:
      • 某行乘以一个非零数
      • 交换两行
      • 某行加上另一行的若干倍
    • 利用初等行变换将方程组化为上三角矩阵
    • 解的情况:
      • 完美阶梯型:唯一解
      • 非完美阶梯型:
        • 0 == 非0:无解
        • 0 == 0:无穷解
    • 步骤:
      • 枚举每一列
        • 找到这一列系数的绝对值最大的一行
        • 将这一行与第一行交换
        • 将改行的第一个数变成一(方程两边同乘某数)
        • 把下面所有行的当前列的系数消成0(某行加上第一行的若干倍)
    • 代码:
      const int N = 110;
      const double esp = 1e-6; //x < esp,则x = 0,否则 x != 0,由于浮点数精度问题,0可能是0.000001
      double a[N][N];
      int n;
      
      int gauss(){
      	int r, c;
      	for(r = 1, c = 1; c <= n; ++ c){//遍历每一列
      		int t = r;
      		for(int i = r; i <= n; ++ i) //找当前列的系数最大的行
      			if(fabs(a[t][c]) < fabs(a[i][c]))
      				t = i; //记录最大行
      		
      		if(fabs(a[t][c]) < esp) continue; //最大行系数为0说明该列系数均为0
      		
      		for(int i = c; i <= n + 1; ++ i) swap(a[r][i], a[t][i]);//否则交换第一行与系数最大行
      		for(int i = n + 1; i >= c; -- i) a[r][i] /= a[r][c]; //将第一行当前列系数变为1
      		
      		for(int i = r + 1; i <= n; ++ i) //将下面所有行的当前列的系数变为0
      			if(fabs(a[i][c]) > esp) //当前列系数非0
      				for(int j = n + 1; j >= c; -- j) 
      					a[i][j] -= a[r][j] * a[i][c];
      
      		r ++ ;
      	}
      	
      	if(r <= n){
      		for(int i = r; i <= n; ++ i)
      			if(fabs(a[i][n + 1]) > esp) return 0; //无解
      		return 1; //无数解
      	}
      	
      	for(int i = n; i >= 1; -- i) //从后往前求解
      		for(int j = i + 1; j <= n; ++ j)
      			a[i][n + 1] -= a[i][j] * a[j][n + 1];
      			
      	return 2; //唯一解
      }
      int main(){
      	cin >> n;
      	
      	for(int i = 1; i <= n; ++ i)
      		for(int j = 1; j <= n + 1; ++ j)
      			cin >> a[i][j];
      	
      	int t = gauss();
      	
      	if(t == 1) cout << "Infinite group solutions";
      	else if(t == 0) cout << "No solution";
      	else if(t == 2){
      		for(int i = 1; i <= n; ++ i) 
      			printf("%.2f\n", a[i][n + 1]);
      	}
      	
      	return 0;
      }
      
  • 求组合数

    • \(C_a^b = \frac{a(a-1)(a-2)···(a-b+1)}{b!}=\frac{a!}{b!(a-b)!}\)
    • 一般直接用公式求组合数容易超时
    • 故可以通过预处理的方式,用递推式求出所有可能的组合数
    • 递推式:\(C_a^b = C_{a-1}^{b-1} + C_{a-1}^{b}\) \(O(N^2)\)
    • 当a,b <= 2e3,询问n = 1e4次时
    • 代码:
      //询问10000次
      //a,b <= 2000
      const int N = 2e3 + 10;
      const int mod = 1e9 + 7;
      int c[N][N];
      int n;
      
      void init(){
          for(int i = 0; i < N; ++ i)
              for(int j = 0; j <= i; ++ j)
                  if(!j) c[i][j] = 1;
                  else c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % mod;
      }
      
    • 当a,b <= 1e5,询问n = 1e4次,模数p = 1e9 + 7时
      • 开二维数组会爆内存,于是利用公式,预处理出阶乘以及阶乘的逆\(O(Nlog(N))\)
      • 代码:
        typedef long long LL;
        const int N = 1e5 + 10;
        const int mod = 1e9 + 7;
        int fact[N], infact[N];
        int n;
        
        //快速幂
        int qmi(int a, int b, int p){
            int res = 1;
            while(b){
                if(b & 1) res = (LL)res * a % p;
                a = (LL)a * a % p;
                b >>= 1;
            }
            return res;
        }
        
        //预处理各阶乘以及阶乘的逆
        void init(){
            fact[0] = infact[0] = 1;
            for(int i = 1; i < N; ++ i){
                fact[i] = (LL)fact[i - 1] * i % mod; //递推式n! = (n - 1)! * n;
                infact[i] = (LL)infact[i - 1] * qmi(i, mod - 2, mod) % mod; //递推式(n!)^-1 = ((n - 1)!)^-1 * n^-1,取模意义下的逆就是逆元
            }
        }
        
        int main(){
            cin >> n;
            init();
            while(n -- ){
                int a, b;
                cin >> a >> b;
                cout << (LL)fact[a] * infact[b] % mod * infact[a - b] % mod << endl;//两个int最大值相乘不会爆longlong,但是三个相乘会爆
            }
            return 0;
        }
        
    • 当a,b <= 1e18,询问n = 2e5次,模数p不定时
      • 两数相乘会爆longlong,利用卢卡斯定理预处理组合数 \(O(plog(N)log(p))\)
      • 定理:\(C_a^b\equiv C_{a \mod p}^{b \mod p}·C_{a/p}^{b/p} \pmod p\)
      • 代码:
        typedef long long LL;
        int p;
        int n;
        
        //快速幂求逆元
        int qmi(int a, int b){
            int res = 1;
            while(b){
                if(b & 1) res = (LL)res * a % p;
                a = (LL)a * a % p;
                b >>= 1;
            }
            return res;
        }
        
        int C(int a, int b){ //求组合数C_a^b
            if(a < b) return 0; //可以不要
            int x = 1, y = 1; //分子分母
            for(int i = 0; i < b; ++ i){
                x = (LL)x * (a - i) % p; //求分子
                y = (LL)y * (i + 1) % p; //求分母
            }
            return (LL)x * qmi(y, p - 2) % p;
        }
        
        //卢卡斯定理
        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; //用卢卡斯定理
        }
        
        int main(){
            cin >> n;
            while(n -- ){
                LL a, b;
                cin >> a >> b >> p;
                cout << lucas(a, b) << endl;
            }
            return 0;
        }
        
    • 当a,b <= 5e5,询问一次,不取模时
      • 此时组合数较大,需要用高精度存储结果
      • 阶乘中素数的个数:
        • \(v_p(n!) = \sum_{i=1}^{\infty} \left\lfloor \frac{n}{p^i} \right\rfloor\)
        • 证明:将 \(n!\) 记为 \(1\times 2\times \cdots \times p\times \cdots \times 2p\times \cdots \times \lfloor n/p\rfloor p\times \cdots \times n\) 那么其中 \(p\) 的倍数有 \(p\times 2p\times \cdots \times \lfloor n/p\rfloor p=p^{\lfloor n/p\rfloor }\lfloor n/p\rfloor !\) 然后在 \(\lfloor n/p\rfloor !\) 中继续寻找 \(p\) 的倍数即可,这是一个递归的过程
      • 代码:
        const int N = 5e3 + 10;
        int primes[N], cnt;
        int st[N];
        int sum[N];
        
        //线性筛,1-n中质数就是n!中的质因子
        void get_primes(int a){
            for(int i = 2; i <= a; ++ i){
                if(!st[i]) primes[ ++ cnt] = i;
                for(int j = 1; primes[j] <= a / i; ++ j){
                    st[primes[j] * i] = 1;
                    if(i % primes[j] == 0) break;
                }
            }
        }
        
        //求a!中质数p的个数
        int get(int a, int p){
            int res = 0;
            while(a){
                res += a / p;
                a /= p;
            }
            return res;
        }
        
        //高精度乘法
        vector<int> mul(vector<int> &A, int b){
            vector<int> c;
            int t = 0;
            for(int i = 0; i < A.size() || t; ++ i){
                if(i < A.size()) t += A[i] * b;
                c.push_back(t % 10);
                t /= 10;
            }
            while(c.size() > 1 && c.back() == 0) c.pop_back();
            return c;
        }
        
        int main(){
        	int a, b;
            cin >> a >> b;
            get_primes(a);
            for(int i = 1; i <= cnt; ++ i){
                int p = primes[i];
                sum[i] = get(a, p) - get(b, p) - get(a - b, p); //a!中质因子个数-b!中质因子个数-(a-b)!中质因子个数
            }
            
            vector<int> res;
            res.push_back(1);
            
            for(int i = 1; i <= cnt; ++ i) //累乘
                for(int j = 1; j <= sum[i]; ++ j)
                    res = mul(res, primes[i]);
            
            for(int i = res.size() - 1; i >= 0; -- i)//输出
                cout << res[i];
            
            return 0;
        }
        
  • 卡特兰数

    • \(C_{2n}^n-C_{2n}^{n-1}=\frac{1}{n+1}·C_{2n}^n\)
    • 用于解决序列排列等问题
    • AcWing 889. 满足条件的01序列 - AcWing
    • 代码:
      #include <iostream>
      
      using namespace std;
      
      typedef long long LL;
      const int N = 1e5 + 10;
      const int p = 1e9 + 7;
      int n;
      
      int qmi(int a, int k){
          int res = 1;
          while(k){
              if(k & 1) res = (LL)res * a % p;
              a = (LL)a * a % p;
              k >>= 1;
          }
          return res;
      }
      
      int main(){
          cin >> n;
          
          int res = 1;
          
          int a = 2 * n, b = n;
          int x = 1, y = 1;
          for(int i = 0; i < b; ++ i){
              x = (LL)x * (a - i) % p;
              y = (LL)y * (i + 1) % p;
          }
          
          res = (LL)x * qmi(y, p - 2) % p;
          res = (LL)res * qmi(n + 1, p - 2) % p;
          cout << res;
          
          return 0;
      }
      

文章来源地址https://www.toymoban.com/news/detail-638449.html

到了这里,关于[数论第三节]高斯消元法/求组合数/卡特兰数的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • MATLAB数值分析学习笔记:线性代数方程组的求解和高斯消元法

    工程和科学计算的许多基本方程都是建立在守恒定律的基础之上的,比如质量守恒等,在数学上,可以建立起形如 [A]{x}={b} 的平衡方程。其中{x}表示各个分量在平衡时的取值,它们表示系统的 状态 或 响应; 右端向量{b}由无关系统性态的常数组成通常表示为 外部激励。 矩阵

    2023年04月15日
    浏览(61)
  • Matlab中求解线性方程组——高斯消元法、LU分解法、QR分解法、SVD分解法、迭代法等

    MATLAB迭代的三种方式以及相关案例举例 MATLAB矩阵的分解函数与案例举例 MATLAB当中线性方程组、不定方程组、奇异方程组、超定方程组的介绍 MATLAB语句实现方阵性质的验证 MATLAB绘图函数的相关介绍——海底测量、二维与三维图形绘制 MATLAB求函数极限的简单介绍 文章目录 前言

    2024年02月08日
    浏览(62)
  • 卡特兰数和算法

    在组合数学中,卡特兰数是一系列自然数,出现在各种组合计数问题中,通常涉及递归定义的对象。它们以比利时数学家尤金·查尔斯·卡特兰(Eugène Charles Catalan)的名字命名。 卡特兰数序列是1, 1, 2, 5, 14, 42......(n = 0,1,...)。 一般来说,用二项式系数表示的第n个卡特兰数

    2024年04月22日
    浏览(28)
  • 洛谷P1722 矩阵Ⅱ——卡特兰数

    传送门: P1722 矩阵 II - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) https://www.luogu.com.cn/problem/P1722 用不需要除任何数的公式来求。

    2024年02月04日
    浏览(52)
  • 浅谈斐波那契数列和卡特兰数

    斐波那契数列是我们较为熟悉的一类数列了,在学习递归和递推的时候我们就已经能求解 (n) 较小的情况了;斐波那契数列的定义如下: [left{begin{matrix}F_{n}=0 n=0\\\\F_{n}=1 n=1\\\\F_{n}=F_{n-1}+F_{n-2} nge 2end{matrix}right.] 卢卡斯数列经常作为一个工具来研究斐波那契数列,所以这里

    2024年02月06日
    浏览(33)
  • 算法基础学习笔记——⑬高斯消元\组合计数\容斥原理

    ✨ 博主: 命运之光 ✨ 专栏: 算法基础学习 目录 ✨高斯消元 ✨组合计数 🍓通过预处理逆元的方式求组合数: 🍓Lucas定理: 🍓分解质因数法求组合数: 前言: 算法学习笔记记录日常分享,需要的看哈O(∩_∩)O,感谢大家的支持! 高斯消元(Gaussian Elimination)是一种用于解线

    2024年02月07日
    浏览(37)
  • 已知n个数的入栈序列,求一共有多少种出栈序列 (卡特兰数)

    这个经典问题有两种解法。 解法一: 设 (f(x)) 为 (x) 个数入栈后,再全部出栈的序列数量 假设我们有 (4) 个数 (a,b,c,d) , 我们来看 (a) 的出栈顺序. 假如 (a) 第一个出栈,那么后面还有 (3) 个数没有出栈,因此方法数是 (f(3)) . 假设 (a) 第二个出栈,那么 (a) 的前面有

    2023年04月23日
    浏览(34)
  • (数字图像处理MATLAB+Python)第七章图像锐化-第三节:高斯滤波与边缘检测

    高斯函数 :是一种常见的连续函数,通常用符号 G ( x ) G(x) G ( x ) 表示。它可以用下面的公式定义 G ( x ) = 1 σ 2 π e − x 2 2 σ 2 G(x)=frac{1}{sigma sqrt{ 2pi }}e^{-frac{x^{2}}{2sigma^{2}}} G ( x ) = σ 2 π ​ 1 ​ e − 2 σ 2 x 2 ​ 其中, x x x 是自变量, σ sigma σ 是一个正实数,表示高斯函

    2024年02月06日
    浏览(52)
  • 矩阵消元法

    消元法(elimination)也是计算机程序解方程的方法,消元法如果奏效了,方程就解出来了。 讨论消元法 奏效 和 失效 的情况。后半部分还介绍了  消元矩阵 ,即我们的消元运算在矩阵乘法中所表现的形式。并从消元矩阵引入,介绍了一些  逆矩阵  的基础知识。 2.1、消元法介

    2023年04月13日
    浏览(39)
  • 2.2 消元法的概念

    消元法 (elimination)是一个求解线性方程组的系统性方法。下面是使用消元法求解一个 2 × 2 2times2 2 × 2 线性方程组的例子。消元之前,两个方程都有 x x x 和 y y y ,消元后,第一个未知数 x x x 将从第二个方程消失: 新的方程 8 y = 8 8y=8 8 y = 8 能够直接得到 y = 1 y=1 y = 1 ,再将

    2024年02月08日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包