数学入门

简单数学模拟

最大公约数 最小公倍数

欧几里得算法(辗转相除法)

gcd(a,b)=gcd(b, a%b); b==0时return b; 最小公倍数 = a / gcd * b;

分数

用 struct 存 up分子 down分母 分子为0分母为1 分母为负上下取相反数 化简上下同除gcd 加减乘除进行模拟 分子分母最好用LL保存(乘法可能越界)

质数

埃氏筛

一个存储质数的数组 一个存储取值范围内每个数是不是质数的bool数组 从遍历取值范围,如果是质数,那么把所有范围内它的质数标记为合数 for(int j = i*i; j <= max; j+=i);

质因子分解

用struct factor存质因子和出现次数 开到10就够了,因为10个就超过int了 遍历之前的质数表(< sqrt(num)),不断用输入的数字除以,如果遍历完还不为0 说明还剩余一个本身目前的值

高精度

大数字可以开成struct 存贮数组和长度 加入构造函数进行初始化

高精度加法
高精度乘法
高精度减法

记得用大的减小的 记得去除前导0

while (c.len - 1 >= 1 && c.d[c.len-1] == 0)
{
    c.len--;
}
高精度除法

从高位开始 每次carry 一个余数 carry *10 + a.d[i]; carry %= b; 记得去除前导0

扩展欧几里得算法

求ax + by = gcd(a,b)的解
int exGcd(int a, int b, int &x, int &y)
{
    if (b == 0)
    {
        x= 1; y = 0;
        return a;
    }
    int g = exGcd(b, a % b, x, y);
    int temp = x;
    x = y;
    y = temp - a / b * y;
    return g;
    
}

得到的x y 为特解 x = x + (b / gcd(a,b)) * 任意整数 y = y - (a / gcd(a,b)) * 任意整数

ax + by = c

如果c % gcd(a,b) == 0 特解x,y为 exgcd结果*(c/gcd(a,b)) x = x + (b / gcd(a,b)) * 任意整数 y = y - (a / gcd(a,b)) * 任意整数

同余式(ax-c)%m == 0

化为 ax + my = c 求解 同样如果(c % gcd(a,m) == 0)有解,且恰好有gcd(a,m)个mod m意义下不同的解; 特解x,y为 exgcd结果*(c/gcd(a,m)) x = x + (m / gcd(a,m)) * 任意整数 y = y - (a / gcd(a,m)) * 任意整数

逆元的求解与(b/a)%m的计算

逆元:(ab-1)%m == 0;a b互为逆元 (b / a )% m = (b * x) % m; a x互为逆元 通过求解(ax - 1)%m==0得到逆元x 如果 1%gcd(a,m)为0就有唯一解

费马小定理

如果m是素数,a为任意整数且a%m!=0,那么 所以逆元就是用快速幂很快就能得到答案

如果都失效怎么办

(b / a) % m = (b % (am)) / a;

组合数

求n!里面的质因子p数目

朴素思想遍历1~n 简化一 n!里面质因子数量 = 可以求0的数量(5的数量)

组合数的计算
  • 朴素模拟21!直接爆
  • 实际上C(m,n)=C(m,n-1)+C(m-1,n-1); 用数组存储结果
  • 也可以牺牲范围用定义式的变形
LL C(LL n, LL m)
{
    LL ans = 1;
    for (LL i = 1;i <= m; i++)
    {
        ans = ans * (n - m + i) / i;
    }
    return 0;
}
组合数的计算%p
方法一 n-1e5 m-1e5

递推的每一步%p

方法二 n-1e6 m-1e6

对定义式进行计算 C(m,n) = n! / m! (n - m)! 计算每个阶乘的每个质因子的数量,用快速幂得到每个质因子次方%p的数量再%p

方法三 n-1e9 m-1e5 p-prime

对定义式的变形计算 m < p p为质数

int C(int n, int m, int p)
{
    int ans = 1;
    for (int i = 1; i <= m; i++)
    {
        ans = ans * (n - m + i) % p;
        ans = ans * inserve(i,p) %p;
    }
    return ans;
}

m >= p p为素数 先统计分子分母里面p的数量 如果分子p比分母多,直接输出0 如果一样多,去除p的成分,再正常计算逆元

int C(int n, int m, int p){
    int ans = 1; numP = 0;
    for (int i = 1; i <=m; i++)
    {
        int tmp = n-m+i;
        while(tmp %p==0)
        {
            numP++;
            tmp/=p;
        }
        ans = ans * tmp % p;
        tmp = i;
        while(tmp % p == 0){
            numP--;
            tmp/=p;
        }
        ans =ans * inverse(tmp, p) % p;
    }
    if (numP > 0) return 0;
    else return ans;
}

如果p不是质数呢 把p质因子分解,求出分子比分母多余的各个质因子,快速幂对这些多余质因子的乘积取余 另一边去除所有质因子后对属于部分逆元计算 也可以把分子分母全质因子分解,得到多余的质因子,进行运算取模。

方法四 n-1e18 m-1e18 p-prime-1e5
int Lucas(int n, int m){
    if (m == 0) return 1;
    return C(n%p,m%p) * Lucas(n/p,m/p) % p;
}