第十二课:高斯消元,四种方式求组合数、卡特兰数

 

目录

一、高斯消元

(1)求解多元线性方程组

(2)求解异或线性方程组

二、组合数

(1)a,b较小,求解

(2)a,b较大,求解

(3)a,b很大,结果模一个给定质数。

Lucas定理

(4)高精度表示结果

三、卡特兰数(Catalan)

(1)定义及计算

(2)例题:


一、高斯消元

(1)求解多元线性方程组

  可以求解多元线性方程组。实际上就是把系数矩阵化为最简阶梯形行列式。

步骤:1.枚举每一个行列,找到该列绝对值最大的一行。

           2.将该列换到当前最上面。3.将改行的第一个元素变为1。

           4.将下面所有行的第c列消为0

           5.用a[i][n](增广矩阵扩出来的那一列)算出并存储答案。

重要的是用代码实现这些流程。

注意点:1.注意c++浮点数表示有误差。2.化为1时,需要从最后一列开始操作。

              3.将-0.00改为0.00。4.运用r++不写到循环内部,是因为某列都为0就直接continue了,最后可以用r

              5.最后从最后一列开始求解。

//这里填你的代码^^
//实际上就是线性代数中的,把系数矩阵通过初等行变换化为最简阶梯形矩阵
#include
#include
#include
using namespace std;const int N =110;
const double eps=1e-8;
double a[N][N];
int n;int gauss()//答案存储在a[i][n]
{int  c,r;//c=colmon为列,r=row为行for(c=0,r=0;cfabs(a[t][c])) t=i;if(fabs(a[t][c])=c;i--) a[r][i]/=a[r][c]; //当前行 首位变为1。注意要从最后一列开始操作for(int i=r+1;ieps)for(int j=n;j>=c;j--)a[i][j]-=a[r][j]*a[i][c];r++;}if(reps)return 2; //无解return 1;//无穷多组解}for(int i=n-1;i>=0;i--)//从最后一行计算结果,用增广矩阵那一列存储答案。for(int j=i+1;j>n;for(int i=0;i>a[i][j];int t=gauss();if(t==2) puts("No solution");else if(t==1) puts("Infinite group solutions");else{for(int i=0;i

(2)求解异或线性方程组

注意一下异或计算答案的过程。

//这里填你的代码^^
#include
using namespace std;const int N =110;
int a[N][N];
int n;int gauss()
{int r,c;for(r=0,c=0;c=0;i--)for(int j=i+1;j>n;for(int i=0;i>a[i][j];int res=gauss();if(res==1) puts("Multiple sets of solutions");else if(res==2) puts("No solution");else{for(int i=0;i

二、组合数

(1)a,b较小,求解C_{a}^{b}\ mod\ (10^{9}+7)

        由于a,b较小,可以利用组合数的定义,根据根据组合数递推公式,运用递推的方式计算出a,b给定范围内的所有组合数。

C_{a}^{b}=C_{a-1}^{b-1}+C_{a-1}^{b}

等式证明:设定x为a集合中一元素,原方案数(从a个元素里面选取b个元素)等价于:不选x元素,从剩下的所有元素里面选取b个元素,加上选取x元素,从剩下的数里面选取b-1个元素。

        预设定C_{a}^{0}=1,即可递推。代码如下:

//这里填你的代码^^
#include
#include
using namespace std;const int N =2010;
int c[N][N];
int mod=1e9+7;
void init()
{for(int i=0;i>n;while(n--){int a, b;cin>>a>>b;cout<

(2)a,b较大,求解C_{a}^{b}\ mod\ (10^{9}+7)

        a,b较大,递推是不现实的。由于结果需要模上一个数,所以可考虑从组合数计算式出发,运用逆元求解。(因为a模p 除以 b模p  不等于 a/b 模p)

C_{a}^{b}\ mod\ (10^9+7)=\frac{a!}{b!(a-b)!} \mod \ (10^9+7)

=a! \ mod \(10^9+7)*b!^{-1}\ mod \ (10^9+7)*(a-b)!^{-1}\ mod \ (10^9+7)

由于1e9+7是一个质数,因此逆元可以用费马小定理和快速幂求解。(否则需要扩展欧几里得算法)首先运用递推求出a,b范围内的数的阶乘模10^9+7的值fact,与其逆元infact。然后根据定义计算。

代码如下:

//这里填你的代码^^
#include
using namespace std;typedef long long LL;
const int N =1e5+10;
int fact[N],infact[N],mod=1e9+7;int  qmi(int a,int b,int q)
{int res=1;while(b){if(b&1) res=(LL)res*a%q;b>>=1;a=(LL)a*a%q;}return res;
}int main()
{fact[0]=infact[0]=1;for(int i=1;i>n;while(n--){int a,b;cin>>a>>b;int t=(LL)fact[a]*infact[a-b]%mod  *infact[b]%mod;   //注意这两个modcout<

(3)a,b很大,结果模一个给定质数。

Lucas定理

C_{a}^{b}\equiv \frac{C_{a/p}^{b/p}}{C_{a\ mod\ p}^{b\ mod\ p}}(mod\ p)

其中p为质数。详细证明及解释见https://zhuanlan.zhihu.com/p/452976974。

运用Lucas定理,我们可以直接根据定义计算:

 

//这里填你的代码^^
#include
#include
using namespace std;
typedef long long LL;int qmi(int a, int k, int p)
{int res = 1;while (k){if (k & 1) res = (LL)res * a % p;a = (LL)a * a % p;k >>= 1;}return res;
}int c(int a,int b,int p)
{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*qmi(i,p-2,p)%p;}return res;
}int lucas(LL a,LL b,int p)
{if(a>n;while(n--){LL a,b;int p;cin>>a>>b>>p;cout<

(4)高精度表示结果

如果无脑直接用高精度写需要实现阶乘、乘法、除法,过于麻烦。于是改进计算流程,只需要实现高精度乘法。

首先分解分解质因数:

C_{a}^{b}=\frac{a!}{b!(a-b)!}=p_{1}^{a_{1}}*p_{2}^{a_{2}}...p_{n}^{a_{n}}

质数可以使用线筛法求得。接下来只需要求出各个质数出现的次数。求法如下:

cnt_{p} (a!)=[\frac{n}{p}]+[\frac{n}{p^{2}}]+[\frac{n}{p^{3}}]+...+[\frac{n}{p^{n}}]

即p的倍数在1到a出现的次数+p方的倍数在1到a出现的次数+······。以n=2举例:某些数是p的倍数,乘起来,则首先p的次数为这些数的个数。同时这里面又有些数是p方的倍数,于是p的次数加上p方的倍数的个数(即1*恰好只是1次方的倍数的数+2*恰好只是二次方的倍数的数)。

因此组合数C_{a}^{b}里p出现的次数:

cnt=cmt(a!)-cnt((a-b)!)-cnt(b!)

代码如下:

//这里填你的代码^^
#include
#include
using namespace std;
const int N =5010;
int primes[N],cnt;
bool st[N];
int sum[N];void get_primes(int n)
{for(int i=2;i<=n;i++){if(!st[i])  primes[cnt++]=i;for(int j=0;primes[j]<=n/i;j++){st[primes[j]*i]=true;if(i%primes[j]==0)  break;}}
}int get(int n,int p)
{int res=0;while(n){res+=n/p;n/=p;}return res;
}vector mul(vector a,int b)
{int t=0;vector c;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_primes(a);for(int i=0;i res;res.push_back(1);for(int i=0;i=0;i--)cout<

三、卡特兰数(Catalan)

(1)定义及计算

(2)例题:

 原题可抽象为一个路径选择问题:(例中取n为6)

如图:0表示向右走一格,1表示向左走一格。根据题目要求,x>=y,即所选则路线不得穿过红线。回答从(0,0)走到(6,6)的走法的个数。

为求解,我们画出一条穿过红线的路径(橙线),再根据红线做其对称线,则对称线的终点为(5,7)。

 由于从(0,0)到(5,7)的路径必然经过红线,在穿过点后以红线为对称轴,做出对称线,则该对称线就是一条非法路径。同样的,每一条路径都可以一一对应一条非法路径。所有非法路径也都可以相应的对应回去。所以,有多少条从(0,0)到(5,7)的路径,就有多少条非法路径。

根据高中排列组合知识:

ans=C_{12}^{6}-C_{12}^{5}

其通项为:

ans=C_{2n}^{n}-C_{2n}^{n-1}

得通项则该题易解:

//这里填你的代码^^.
#include
using namespace std;
typedef long long LL;
int mod=1e9+7;
const int N =100010;int qmi(int a,int b,int p)
{int res=1;while(b){if(b&1) res=(LL)res*a%p;b>>=1;a=(LL)a*a%p;}return res;
}int main()
{int n;cin>>n;int a=2*n,b=n;int res=1;for(int i=a;i>(a-b);i--)  res=(LL)res*i%mod;for(int i=1;i<=b;i++)  res=(LL)res*qmi(i,mod-2,mod)%mod;res=(LL)res*qmi(n+1,mod-2,mod)%mod;cout<


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章