高斯消元

高斯消元两种形式

定义:

使用高斯消元时,我们会碰到两种形式:

正常的高斯消元,没有模数或模数为质数

设枚举了矩阵中的两行:

\[\quad \begin{bmatrix} a_{i,i} & a_{i,i+1} & .... & a_{i,n} \\ a_{j,i} & a_{j,i+1} & .... & a_{j,n} \end{bmatrix} \quad \]

根据高斯消元,我们现在要把每一个 \(a_{i,k}(i \leq k \leq n)\) 乘上 \(\frac{a_{j,i}}{a_{i,i}}\)

然后再把每一个 \(a_{j,k}(i \leq k \leq n)\) 减去 \(a_{i,k}\) ,这样就能消去 \(a_{j,i}\)

简单来说,就是对 \(i \leq k \leq n\) , \(a_{j,k} \rightarrow (a_{j,k}-a_{j,k}\frac{a_{j,i}}{a_{i,i}})\)

这样的高斯消元可以用 \(double\) 直接算,模数为质数情况下也可以利用逆元处理。

代码:

for(int i=1;i<=n;i++){
    int now=i; 
    for(int j=i+1;j<=n;j++) if(fabs(a[j][i])>fabs(a[now][i])) now=j;
    //寻找 a[?][i] 最大的一行,作为辗转相除法的开始
    if(now!=i) for(int j=1;j<=n;j++) swap(a[now][j],a[i][j]);
    for(int j=i+1;j<=n;j++){//枚举下面行
        double mul=a[j][i]/a[i][i];
        for(int k=i;k<=n;k++) a[j][k]-=a[i][k]*mul;
        //对于第j行的 [i,n] ,进行值的减少
    }
}

模数不是质数,例如[HEOI2015]小 Z 的房间

这种情况下,我们只能用辗转相除法解决。

考虑到我们的目的是消除 \(a_{j,i}\) ,因此可以对矩阵进行两行交换,一行减去另一行的若干倍。

还是同样的矩阵,我们可以把 \(a_{i,k}\) 减去 \(a_{j,k}\lfloor\frac{a_{i,i}}{a_{j,i}}\rfloor(i \leq k \leq n)\) ,然后再交换第 \(i\) 和第 \(j\) 行。

辗转相除到最后,肯定有 \(a_{j,i}=0\)

具体解释可以看这道例题的第一篇题解,对辗转相除法作了比较好的解释。

代码:

for(int i=1;i<cnt;i++){
    for(int j=i+1;j<=cnt;j++){//枚举矩阵每一行
        while(a[j][i]){//达成目的
            int mul=a[i][i]/a[j][i];
            for(int k=i;k<=cnt;k++) a[i][k]=(a[i][k]-mul*a[j][k]%mod+mod)%mod;
            swap(a[i],a[j]);//上下交换
            ans*=-1;//交换矩阵两行换符号
        }
    }
}

辗转相除法的时间复杂度过大,尽量不用,除非模数是非质数

posted @ 2021-10-09 15:27  Evitagen  阅读(102)  评论(0编辑  收藏  举报