P3389 【模板】高斯消元法、P4783 【模板】矩阵求逆、P3807 【模板】卢卡斯定理/Lucas 定理

P3389 【模板】高斯消元法

题目描述

P3389 【模板】高斯消元法 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

运行代码

#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;

const int N = 110;
const double eps = 1e-6;
int n;
double a[N][N];  // 增广矩阵

// 交换两行
void swapRows(int row1, int row2) {
    for (int j = 1; j <= n + 1; j++) {
        swap(a[row1][j], a[row2][j]);
    }
}

// 高斯消元函数
int gauss() {
    for (int i = 1; i <= n; ++i) {  // 第 i 主元
        int nonzeroRow = -1;
        for (int k = i; k <= n; ++k) {  // 找非零行
            if (fabs(a[k][i]) > eps) {
                nonzeroRow = k;
                break;
            }
        }
        if (nonzeroRow == -1) return 0;  // 无解

        swapRows(i, nonzeroRow);  // 交换非零行

        double div = a[i][i];
        for (int j = n + 1; j >= i; j--) {  // 使主元所在列的值变为 1
            a[i][j] /= div;
        }

        for (int k = i + 1; k <= n; k++) {  // 使主元所在列下方的值变为 0
            double mult = a[k][i];
            for (int j = n + 1; j >= i; j--) {
                a[k][j] -= mult * a[i][j];
            }
        }
    }

    for (int i = n - 1; i >= 1; i--) {  // 回代
        for (int j = i + 1; j <= n; j++) {
            a[i][n + 1] -= a[i][j] * a[j][n + 1];
        }
    }

    return 1;  // 存在唯一解
}

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n + 1; j++) {
            scanf("%lf", &a[i][j]);
        }
    }

    if (gauss()) {
        for (int i = 1; i < n + 1; i++) {
            printf("%.2lf\n", a[i][n + 1]);
        }
    }
    else {
        puts("No Solution");
    }

    return 0;
}

代码思路

  1. 定义了一些常量和变量:

    • N 表示矩阵的最大大小。
    • eps 是一个极小的误差阈值,用于判断数值是否接近零。
    • n 表示方程组的未知数个数,也是矩阵的行数。
    • a[N][N] 是存储增广矩阵的二维数组。
  2. gauss 函数:

    • 外层循环 for(int i=1; i<=n; ++i) 遍历每一列,以确定主元所在的行。
    • 内层第一个循环 for(int k=i; k<=n; ++k) 用于在当前列中寻找非零行,如果找到则与当前行交换。
    • 如果主元所在行的主元值接近零(fabs(a[i][i])<eps),则表示方程组无解,函数返回 0 。
    • 接下来,通过除以主元值,将主元所在列的主元变为 1(for(int j=n+1; j>=i; j--) a[i][j] /= a[i][i];)。
    • 然后通过内层的双重循环,将主元所在列下方的元素变为 0(for(int k=i+1; k<=n; k++) for(int j=n+1; j>=i; j--) a[k][j]-=a[k][i]*a[i][j];)。
    • 最后,通过回代过程(for(int i=n-1; i>=1; i--))求解出未知数的值。
  3. main 函数:

    • 首先读取未知数的个数 n 。
    • 然后通过双重循环读取增广矩阵的元素值。
    • 调用 gauss 函数进行求解,如果返回 1 表示有唯一解,输出解的值;否则输出 "No Solution" 表示无解。

P4783 【模板】矩阵求逆

题目描述

P4783 【模板】矩阵求逆 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

运行代码

#include <iostream>
#include <cstdio>
#include <cmath>
#define LL long long
using namespace std;

const int N = 405, P = 1e9 + 7;
int n;
LL a[N][N << 1];

LL quickPow(LL a, LL b) {
    LL ans = 1;
    while (b) {
        if (b & 1) ans = ans * a % P;
        a = a * a % P;
        b >>= 1;
    }
    return ans;
}

// 交换两行
void swapRows(int row1, int row2) {
    for (int j = 1; j <= 2 * n; j++) {
        swap(a[row1][j], a[row2][j]);
    }
}

// 高斯-约旦消元函数
bool GaussJordan() {
    for (int i = 1; i <= n; ++i) {  // 枚举主元的行列
        int nonzeroRow = -1;
        for (int k = i; k <= n; ++k) {  // 找非 0 行
            if (a[k][i]) {
                nonzeroRow = k;
                break;
            }
        }
        if (nonzeroRow == -1) return false;  // 无解

        swapRows(i, nonzeroRow);  // 换行

        LL inv = quickPow(a[i][i], P - 2);  // 求逆元

        for (int k = 1; k <= n; ++k) {  // 对角化
            if (k == i) continue;
            LL factor = a[k][i] * inv % P;
            for (int j = i; j <= 2 * n; ++j) {
                a[k][j] = ((a[k][j] - factor * a[i][j]) % P + P) % P;
            }
        }
        for (int j = 1; j <= 2 * n; ++j) {  // 除以主元
            a[i][j] = a[i][j] * inv % P;
        }
    }
    return true;
}

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= n; ++j) {
            scanf("%lld", &a[i][j]);
            a[i][i + n] = 1;
        }
    }

    if (GaussJordan()) {
        for (int i = 1; i <= n; ++i) {
            for (int j = n + 1; j <= 2 * n; ++j) {
                printf("%lld ", a[i][j]);
            }
            puts("");
        }
    } else {
        puts("No Solution");
    }

    return 0;
}

代码思路

  1. 定义了一些常量和变量:

    • N 表示矩阵的最大大小。
    • P 是一个大质数,用于取模运算。
    • n 表示矩阵的行数。
    • a[N][N << 1] 是一个二维数组,用于存储矩阵。
  2. quickPow 函数:这是一个用于快速计算幂次的函数,通过位运算和循环来实现高效的幂运算,并取模 P 。

  3. Gauss_Jordan 函数:

    • 这是高斯-约旦消元法的实现函数。
    • 外层循环 for(int i=1;i<=n;++i) 遍历每一列,以确定主元所在的行。
    • 内层第一个循环 for(int k=i; k<=n; ++k) 用于在当前列中寻找非零行,如果找到则将其与当前行交换。
    • 如果主元所在行的主元值为 0,则表示方程组无解,函数返回 0 。
    • 计算主元的逆元 x = quickPow(a[i][i], P - 2) 。
    • 然后通过内层循环,对于非主元行进行消元操作,使得主元列除主元外的元素变为 0 。
    • 最后,将主元所在行的元素除以主元。
  4. main 函数:

    • 首先读取矩阵的行数 n 。
    • 通过双重循环读取矩阵的元素值,并将单位矩阵附加在原始矩阵的右侧。
    • 调用 Gauss_Jordan 函数进行求解,如果返回 true 表示有解,输出求解后的结果;否则输出 "No Solution" 表示无解。

P3807 【模板】卢卡斯定理/Lucas 定理

题目描述

P3807 【模板】卢卡斯定理/Lucas 定理 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

运行代码

#include <iostream>
using namespace std;

typedef long long LL;
const int N = 100010;
LL f[N], g[N];

LL qpow(LL a, int b, int p) {
    LL res = 1;
    a %= p;
    while (b) {
        if (b & 1) res = res * a % p;
        a = a * a % p;
        b >>= 1;
    }
    return res;
}

void init(int p) {
    f[0] = g[0] = 1;
    for (int i = 1; i <= p; i++) {
        f[i] = f[i - 1] * i % p;
        g[i] = qpow(f[i], p - 2, p);
    }
}

LL getC(int n, int m, int p) {
    if (m > n) return 0;
    return f[n] * g[m] % p * g[n - m] % p;
}

int lucas(LL n, LL m, int p) {
    if (m == 0) return 1;
    return lucas(n / p, m / p, p) * getC(n % p, m % p, p) % p;
}

int main() {
    int q;
    cin >> q;
    while (q--) {
        LL n, m;
        int p;
        cin >> n >> m >> p;
        init(p);
        printf("%d\n", lucas(n + m, n, p));
    }
    return 0;
}

代码思路

  1. 定义了一些常量和数据类型:N 用于设定一些数组的大小上限。定义了 LL 类型(通常表示长整型)用于处理较大的整数。

  2. qpow 函数:这是一个快速幂函数,用于计算 a 的 b 次幂对 p 取模的结果。通过位运算和循环,逐位判断 b 的二进制位,根据是否为 1 来决定是否累乘当前的 a ,同时不断更新 a 为其平方对 p 取模的结果。

  3. init 函数:初始化两个数组 f 和 g 。f 数组用于存储从 0 到 p 的阶乘对 p 取模的结果。g 数组用于存储从 0 到 p 的阶乘的逆元对 p 取模的结果。通过不断累乘计算阶乘,并使用快速幂计算阶乘的逆元。

  4. getC 函数:用于计算组合数 C(n, m) 对 p 取模的结果。首先判断 m 是否大于 n ,如果是则返回 0 。然后通过已计算好的 f 和 g 数组来计算组合数。

  5. lucas 函数:这是卢卡斯定理的实现,用于计算较大数的组合数对 p 取模的结果。如果 m 为 0 ,则直接返回 1 。否则,将 n 和 m 分别除以 p ,递归计算卢卡斯定理的子问题,并乘以当前位的组合数 getC(n % p, m % p, p) 对 p 取模的结果。

  6. main 函数:首先读取询问的次数 q 。在每次询问中,读取 n 、 m 和 p 。调用 init 函数初始化相关数组。最后计算并输出 lucas(n + m, n, p) 的结果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

筱姌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值