算法基础-数学知识-高斯消元、求组合数

高斯消元、求组合数

  • 高斯消元
    • 883. 高斯消元解线性方程组
  • 组合数
    • AcWing 885. 求组合数 I
    • AcWing 886. 求组合数 II
    • AcWing 887. 求组合数 III
    • AcWing 888. 求组合数 IV

高斯消元

  1. 找到当前列绝对值最大的数 所在的行
  2. 将改行的该列的系数变成1,其他列也要跟着变
  3. 将这行和最上面未处理的那行交换(不是第一行)
  4. 最上面那行的以下的所有行的该列消元
  5. 判断是否存在解 123三种情况
  6. 若有唯一解,则从最下面开始消元

883. 高斯消元解线性方程组

#include 
#include 
#include 

using namespace std;
const int N = 110;
const double eps = 1e-6;//eps是epsilon,表示很小的值
double a[N][N];
int n;
int gauss()
{
    int c, r;
    for (c = 0, r = 0; c < n; ++ c) // ++ r不能放在这一行,因为不是每一个a[t][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]) < eps) continue;//说明就没有未知数Xc,他的系数最大就是0
        //交换
        for (int i = c; i <= n; ++ i) swap(a[t][i], a[r][i]);
        //将这一行的这一列变成1
        for (int i = n; i >= c; -- i) a[r][i] /= a[r][c];//后面的状态需要第一个状态作为基准,因此从后面推

        //将下面的行对应的这一列消成0
        for (int i = r + 1; i < n; ++ i)
        {
            if (fabs(a[i][c]) > eps)
                for (int j = n; j >= c; -- j)
                    a[i][j] -= a[r][j] * a[i][c];
        }

        r ++;
    }
    //c此时=n
    if (r < c)// 说明剩下方程的个数是小于 n 的,说明不是唯一解,判断是无解还是无穷多解
    {// 因为已经是阶梯型,所以 r ~ n-1 的值应该都为 0
        for (int i = r; i < n; i ++ )// 
            if (fabs(a[i][n]) > eps)// a[i][n] 代表 b_i ,即 左边=0,右边=b_i,0 != b_i, 所以无解。
                return 2;
        return 1;// 否则, 0 = 0,就是r ~ n-1的方程都是多余方程
    }
    for (int i = r; i >= 0; -- i)//r此时=n-1
        for (int j = i + 1; j < n; ++ j)
            a[i][n] -= a[i][j] * a[j][n];
            
    return 0;
}
void solve()
{
    cin >> n;
    for (int i = 0; i < n; ++ i)
        for (int j = 0; j <= n; ++ j)
            cin >> a[i][j];   
    int t = gauss();
    if (t == 1) puts("Infinite group solutions");
    else if (t == 2) puts("No solution");
    else  
    {
        for (int i = 0; i < n; i ++ ) printf("%.2lf\n", a[i][n]);
    }
}
int32_t main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    int T = 1;
    //cin >> T;
    while (T --) solve();
    return 0;
}

组合数

算法基础-数学知识-高斯消元、求组合数_第1张图片

  1. 递推就是Cba = Cba-1 + Cb-1a-1
  2. 预处理就是qmi (logN复杂度)结合模运算里面的逆元C^b^~a~ = a! / ((a-b)!*b!),提前将所有阶乘的逆元和阶乘的值算出来
  3. lucas定理证明我看了懵懵懂懂,记住结论就行

AcWing 885. 求组合数 I

#include 
#include 
#include 

using namespace std;
const int N = 2e3 + 10, mod = 1e9 + 7;
int c[N][N];
void init()
{
    for (int i = 0; i < N; ++ i)
        for (int j = 0; j <= i; ++ j)
            if (j == 0) c[i][j] = 1;//i == j的情况可以通过前面处理好的递推出来
            else c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
}
void solve()
{
    int n;
    cin >> n;
    init();
    while (n -- )
    {
        int a, b;
        cin >> a >> b;
        cout << c[a][b] << endl;
    }
}
int32_t main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    int T = 1;
    //cin >> T;
    while (T --) solve();
    return 0;
}

AcWing 886. 求组合数 II

  1. 运用组合数的定义,即最基本的运算公式。
  2. 基本运算公式中含有除法,我们想把除法变成乘法。
  3. 前面说了除法变乘法,本题正好有取模预算,取模运算下除法变乘法可以用欧拉函数扩展。
  4. 模又是质数,可以用费马定理,费马定理可以用快速幂将N优化为logN复杂度
#include 
#include 
#include 

using namespace std;
typedef long long LL;
const int N = 1e5 + 10, mod = 1e9 + 7;
int fact[N];//fact[i]表示i的阶乘
int infact[N];//infact[i]表示i阶乘的逆元,可以由infact[i - 1] * i的逆元获得
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;
}
void solve()
{
    int n;
    cin >> n;
    fact[0] = infact[0] = 1;
    for (int i = 1; i < N; i ++ )
    {
        fact[i] = (LL)fact[i - 1] * i % mod;//记得多次取模
        infact[i] = (LL)infact[i - 1] * qmi(i, mod - 2, mod) % mod;
    }
    while (n -- )
    {
        int a, b;
        cin >> a >> b;
        printf("%d\n", (LL)fact[a] * infact[b] % mod * infact[a - b] % mod);
    }
}
int32_t main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    int T = 1;
    //cin >> T;
    while (T --) solve();
    return 0;
}

AcWing 887. 求组合数 III

算法基础-数学知识-高斯消元、求组合数_第2张图片

#include 
#include 
#include 

using namespace std;
typedef long long LL;

int qmi(int a, int k, int p)
{
    LL res = 1;
    while (k)
    {
        if (k & 1) res = res * a % p;
        a = (LL)a * a % p;
        k >>= 1;
    }
    return res;
}
int C(LL a, LL b, int p)//好坑,a和b可能是LL
{
    if (a < b) return 0;
    if (b > a - b) b = a - b; //Ca b = Ca a - b,可以自己举个例子
    int x = 1, y = 1;
    for (int i = 0; i < b; ++ i)//就是C^b^~a~ = a! / ((a-b)!*b!)
    {
        x = (LL)x * (a - i) % p;
        y = (LL)y * (i + 1) % p;
    }
    return (LL)x * qmi(y, p - 2, p) % p;//p一定为质数
}

int lucas(LL a, LL b, int p)
{
    if (a < p && b < p) return C(a, b, p);
    return (LL)C(a % p, b % p, p) * lucas(a / p, b / p, p) % p;
}

void solve()
{
    int n;
    cin >> n;
    LL a, b;
    int p;
    while (n -- )
    {
        cin >> a >> b >> p;//好坑,a和b可能是LL
        cout << lucas(a, b, p) << endl;
    }    
}
int32_t main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    int T = 1;
    //cin >> T;
    while (T --) solve();
    return 0;
}

AcWing 888. 求组合数 IV

算法基础-数学知识-高斯消元、求组合数_第3张图片

#include 
#include 
#include 

using namespace std;
const int N = 5e3;
int primes[N], cnt;
int sum[N];
bool st[N];
void init(int a)//初始化获得1-a之间所有的质数,数论里面学过 a一定可以分解成质因数的乘积
{
    for (int i = 2; i <= a; ++ i)
    {
        if (!st[i]) primes[cnt++] = i;
        for (int j = 0; primes[j] <= a / i; ++ j)
        {
            st[i * primes[j]] = true;
            if (i % primes[j] == 0) break;
        }       
    }
}

int get(int x, int p)//获得每个质因数的数量
{
    int res = 0;
    while (x)
    {
        res += x / p;
        x /= p;
    }
    return res;
}

vector<int> mul(vector<int> a, int b) //高精度乘法
{
    int t = 0;
    vector<int> 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;
}

void solve()
{
    int a, b;
    cin >> a >> b;
    init(a);   
    for (int i = 0; i < cnt; ++ i)//计算结果中的所有质因数的数量
        sum[i] = get(a, primes[i]) - get(a - b, primes[i]) - get(b, primes[i]);
    

    vector<int> res;
    res.push_back(1);
    for (int i = 0; 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];
}
int32_t main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    int T = 1;
    //cin >> T;
    while (T --) solve();
    return 0;
}

你可能感兴趣的:(算法,组合数,数据结构,c++,蓝桥杯)