高斯消元法与组合数的奇淫技巧

高斯消元法

高 斯 消 元 法 可 以 在 O ( n 3 ) 的 时 间 复 杂 度 下 求 解 多 元 线 性 方 程 组 高斯消元法可以在O(n^{3})的时间复杂度下求解多元线性方程组 O(n3)线

a 11 x 1 + a 12 x 2 + . . . + a 1 n x n = b 1 a 11 x 1 + a 12 x 2 + . . . + a 1 n x n = b 2 . . . . a n 1 x 1 + a n 2 x 2 + . . . + a n n x n = b n a_{11}x_{1}+a_{12}x_{2}+...+a_{1n}x_{n}=b_{1}\\ a_{11}x_{1}+a_{12}x_{2}+...+a_{1n}x_{n}=b_{2}\\ ....\\ a_{n1}x_{1}+a_{n2}x_{2}+...+a_{nn}x_{n}=b_{n} a11x1+a12x2+...+a1nxn=b1a11x1+a12x2+...+a1nxn=b2....an1x1+an2x2+...+annxn=bn

解一共有三种情况:
1. 无 解 2. 无 穷 解 3. 有 唯 一 解 1.无解\\ 2.无穷解\\ 3.有唯一解\\ 1.2.3.
可以通过其增广矩阵来解方程组:
[ a 11 a 12 . . . a 1 n a 21 a 22 . . . a 2 n . . . a n 1 a n 2 . . . a n n b 1 b 2 . . . b n ] \left [ \begin{array}{c:c} \begin{matrix} a_{11}&a_{12}&...a_{1n}\\ a_{21}&a_{22}&...a_{2n}\\ ...\\ a_{n1}&a_{n2}&...a_{nn}\\ \end{matrix}& \begin{matrix} b_{1}\\ b_{2}\\ ...\\ b_{n}\\ \end{matrix} \end{array} \right ] a11a21...an1a12a22an2...a1n...a2n...annb1b2...bn
通过初等变换转换为上三角的形式:
a 11 x 1 + a 12 x 2 + . . . + a 1 n x n = b 1 a 12 x 2 + . . . + a 1 n x n = b 2 . . . . a n n x n = b n a_{11}x_{1}+a_{12}x_{2}+...+a_{1n}x_{n}=b_{1}\\ a_{12}x_{2}+...+a_{1n}x_{n}=b_{2}\\ ....\\ a_{nn}x_{n}=b_{n} a11x1+a12x2+...+a1nxn=b1a12x2+...+a1nxn=b2....annxn=bn
对应增广矩阵为;
[ a 11 a 12 . . . a 1 n 0 a 22 . . . a 2 n . . . 0 0 . . . a n n b 1 b 2 . . . b n ] \left [ \begin{array}{c:c} \begin{matrix} a_{11}&a_{12}&...a_{1n}\\ 0&a_{22}&...a_{2n}\\ ...\\ 0&0&...a_{nn}\\ \end{matrix}& \begin{matrix} b_{1}\\ b_{2}\\ ...\\ b_{n}\\ \end{matrix} \end{array} \right ] a110...0a12a220...a1n...a2n...annb1b2...bn

高斯消元思路

枚举每一列:

  • 找到绝对值最大的一行
  • 将这一行换到最上面去
  • 将该行的第c个数变成1
  • 用第一行将下面所有行的第c列消成0
int gauss(){
    int c,r;
    for(c=1,r=1;c<=n;c++){
        int t=r;
        //第一步找到最大的数
        for(int i=r;i<=n;i++)
            if(fabs(a[i][c])>fabs(a[t][c]))
                t=i;
        
        //第二步符合条件的就交换到第一行
        if(fabs(a[t][c])<eps) continue;
        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];
        
        //逐行减去
        for(int i=r+1;i<=n;i++)
            if(fabs(a[i][c])>eps)
                for(int j=n+1;j>=c;j--)
                    a[i][j]-=a[i][c]*a[r][j];
        r++;
    }
    if(r<=n){
        for(int i=r;i<=n;i++) //遍历每一行
            if(fabs(a[i][n+1])>eps) return 2;
        return 1;
    }
    for(int i=n-1;i>=1;i--)  //从最后一行开始
        for(int j=i+1;j<=n;j++) //从下一行开始
            a[i][n+1]-=a[j][n+1]*a[i][j];
    return 0;
}

组合数

定义法:

C a b = a ! b ! ⋅ ( a − b ) ! C_{a}^{b}=\frac{a!}{b! \cdot (a-b)!} Cab=b!(ab)!a!

迭代公式法:

C a b = C a − 1 b + C a − 1 b − 1 C_{a}^{b}=C_{a-1}^{b}+C_{a-1}^{b-1} Cab=Ca1b+Ca1b1

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>

#define MAXN 2010
const int mod=1e9+7;
using namespace std;

int C[MAXN][MAXN];
int a,b,n;

int main(){
    for(int i=0;i<MAXN;i++)
        C[i][0]=C[i][i]=1,C[i][1]=i;
    //递推求解组合数
    for(int i=1;i<MAXN;i++)
        for(int j=1;j<i;j++)
            C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;

    scanf("%d",&n);
    while(n--){
        scanf("%d%d",&a,&b);
        printf("%d\n",C[a][b]);
    }
    return 0;
}
卢卡斯定理:

C a b ≡ C a   m o d   p b   m o d   p ⋅   C a p b p ( m o d   p ) C_{a}^{b} \equiv C_{a\ mod \ p}^{b\ mod \ p} \cdot \ C_{\frac{a}{p}}^{\frac{b}{p}} (mod\ p) CabCa mod pb mod p Cpapb(mod p)

int lucas(ll a,ll b){
    if(a<p&&b<p) return C(a,b);

    int res= (ll)C(a%p,b%p)*lucas(a/p,b/p)%p;
    return res;
}
//C(a,b)即以a为底取b的组合数
组合数高精度
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define MAXN 5010 
using namespace std;

bool numlist[MAXN];
int prime[MAXN],cnt=0,num[MAXN];
int a,b;
vector<int> res;
//欧拉筛筛出所有质数
void Eular(){
    for(int i=2;i<=5001;i++){
        if(numlist[i]==0){
            prime[++cnt]=i;
        }
        for(int j=1;j<=cnt&&prime[j]<=5001/i;j++){
            numlist[i*prime[j]]=true;
            if(i%prime[j]==0) break;
        }
    }
}
//高精度
void mult(int a){
    for(int i=0;i<res.size();i++)
        res[i]*=a;
    for(int i=0;i<res.size()-1;i++){
        if(res[i]>=10){
            res[i+1]+=res[i]/10;
            res[i]%=10;
        }
    }
    while(res[res.size()-1]>=10){
        int u=res.size()-1;
        res.push_back(res[u]/10);
        res[u]%=10;
    }
}

int main(){
    Eular();
    scanf("%d%d",&a,&b);
    //处理出所有的公约数
    for(int i=1;i<=cnt;i++){
        int u=a;
        while(u){
            num[i]+=u/prime[i];
            u/=prime[i];
        }
        u=b;
        while(u){
            num[i]-=u/prime[i];
            u/=prime[i];
        }
        u=a-b;
        while(u){
            num[i]-=u/prime[i];
            u/=prime[i];
        }
    }
    res.push_back(1);
    //高精度乘法
    for(int i=1;i<=cnt;i++){
        if(num[i]!=0){
            for(int j=1;j<=num[i];j++){
                mult(prime[i]);
            }
        }
    }
    for(int i=res.size()-1;i>=0;i--)
        printf("%d",res[i]);
    puts("");
    return 0;
}
卡特兰数

对于一个只由01构成的长度为2n的序列,要求其任意前缀都有0的个数大于1的个数,因此可以通过转化为在网格图中的路径表示来证明一共有:
C 2 n n − C 2 n n − 1 = C 2 n n ⋅   1 n + 1 C_{2n}^{n}-C_{2n}^{n-1}=C_{2n}^{n}\cdot\ \frac{1}{n+1} C2nnC2nn1=C2nn n+11
种方案数

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值