算法模板-数学

质数

分解质因数

// 分解质因数,并按照质因数从小到大的顺序输出每个质因数的底数和指数。
// 设x=sqrt(n),则n的大于x的质因数最多有一个,因此时间复杂度为O(N^(1/ 2)),最后进行特判即可
void solve(int x)
{
    for(int i = 2; i <= x / i; i ++)
        if(x % i == 0)
        {
            int cnt = 0;
            while(x % i == 0)
            {
                cnt ++;
                x /= i;
            }
            printf("%d %d\n", i, cnt);
        }
    if(x > 1) printf("%d %d\n", x, 1);
}

线性筛

// O(N)的时间复杂度内获取[1,n]的所有素数
// 求积性函数,例如欧拉函数
void get_primes(int n)
{
    for(int i = 2; i <= n ; i ++)
    {
        // 未被筛过即为质数
        if(!st[i]) primes[cnt ++] = i;
        for(int j = 0; primes[j] * i <= n; j ++)
        {
            //筛除以primes[j]为最小质因子的合数
            st[primes[j] * i] = true;
            // 1、当出现 i % primes[j] == 0 时,primes[j] 一定是 i
            // 的最小质因子,因此也一定是 primes[j] * i 的最小质因子。
            // 2、当出现 i % primes[j] != 0 时,说明我们还尚未枚举到 i
            // 的任何一个质因子,也就表示 primes[j] 小于 i 的任何一个
            // 质因子,这时 primes[j] 就一定是 primes[j] * i 的最小质因子。
            if(i % primes[j] == 0) break;
        }
    }
}

约数

求约数

void solve(int x)
{
    cnt = 0;
    for(int i = 1; i <= x / i; i ++)
    {
        if(x % i == 0) factors[cnt ++] = i;
        if(x % (x / i) == 0 && i != x / i) factors[cnt ++] = x / i;
    }
}

约数个数

// 约数个数为所有质因子的幂+1的乘积
unordered_map<int, int> factor_cnt;
void solve(int x)
{
    for(int i = 2; i <= x / i; i ++)
        while(x % i == 0)
        {
            factor_cnt[i] ++;
            x /= i;
        }
    if(x > 1) factor_cnt[x] ++;
    
    int res = 1;
    for(auto it : factor_cnt) res = (LL)res * (it.second + 1) % MOD;
}

约数之和

unordered_map<int, int> factor_cnt;
void solve(int x)
{
    for(int i = 2; i <= x / i; i ++)
        while(x % i == 0)
        {
            factor_cnt[i] ++;
            x /= i;
        }
    if(x > 1) factor_cnt[x] ++;

    int res = 1;
    for(auto it : factor_cnt)
    {
        int p = it.first, cnt = it.second, sum = 1;
        while(cnt --)
        {
            sum = ((LL)sum * p % MOD + 1) % MOD;
        }
        res = (LL)res * sum % MOD;
    }
}


欧几里得算法

// 求a b的最大公因数
int gcd(int a, int b)
{
    return b ? gcd(b, a % b) : a;
}

欧拉函数

1∼N中与N互质的数的个数被称为欧拉函数,记为 ϕ(N)。
若在算数基本定理中,N=p1^a1 * p2^a2 * … * pm^am,则:
ϕ(N) = N×((p1−1)/p1)×((p2−1)/p2)×…×((pm−1)/pm)

公式求欧拉函数

void solve(int x)
{
    cnt = 0;
    int res = x;
    for(int i = 2; i <= x / i; i ++)
        if(x % i == 0)
        {
            factors[cnt ++] = i;
            while(x % i == 0) x /= i;
        }
    if(x > 1) factors[cnt ++] = x;
    for(int i = 0; i < cnt; i ++) res = res / factors[i] * (factors[i] - 1);
    cout << res << endl;
}

筛法求欧拉函数

void get_ou(int n)
{
    ou[1] = 1;
    for(int i = 2; i <= n; i ++)
    {
        if(!st[i])
        {
            primes[cnt ++] = i;
            // i 为素数时,ϕ(i) = (i - 1)
            ou[i] = i - 1;
        }
        for(int j = 0; primes[j] * i <= n; j ++)
        {
            st[i * primes[j]] = true;
            if(i % primes[j] == 0)
            {
                // primes[j] 为 i * primes[j] 的质因子,但该质因子的指数不为1
                // 故ϕ(i * primes[j]) = primes[j] * ϕ(i)
                ou[i * primes[j]] = primes[j] * ou[i];
                break;
            }
            else 
            {
                // primes[j] 为 i * primes[j] 的质因子,且该质因子的指数为1
                // 故 ϕ(i * primes[j]) = primes[j] * ((primes[j] - 1) / primes[j]) * ϕ(i)
                // 即 ϕ(i * primes[j]) = (primes[j] - 1) * ϕ(i)
                ou[i * primes[j]] = (primes[j] - 1) * ou[i];
            }
        }
    }
}

倍增法

快速幂

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

龟速乘

// 求 a * b % p
int mul(int a, int b, int p)
{
    int res = 0;
    while(b)
    {
        if(b & 1) res = (res + a) % p;
        a = (a << 1) % p;
        b >>= 1; 
    }
    return res;
}

扩展欧几里得

// x, y 为一个可行解,使得 ax ≡  d (mod b), 即 ax + by = d
// 通解为:
// x = x0 + kb/d  
// y = y0 - ka/d
int exgcd(int a, int b, int & x, int & y)
{
    if(!b)
    {
        x = 1, y = 0;
        return a;
    }
    int d = exgcd(b, a % b, y, x);
    y -= a / b * x;
    return d;
}

中国剩余定理

公式推导

void solve()
{
    cin >> n;
    LL a1, m1, a2, m2;
    LL x;
    cin >> a1 >> m1;
    for(int i = 0; i < n - 1; i ++)
    {
        cin >> a2 >> m2;
        LL k1, k2;
        LL d = exgcd(a1, a2, k1, k2);
        if((m2 - m1) % d)
        {
            puts("-1");
            return;
        }
        
        k1 *= (m2 - m1) / d;
        k1 = (k1 % (a2 / d) + (a2 / d)) % (a2 / d);
        
        m1 = k1 * a1 + m1;
        a1 = a1 / d * a2;
    }
    cout << m1 << endl;
}

公式计算

令:
1.M = m1 * m2 * m3 * … * mn
2.Mi = M / mi
3.ti是Mi关于mi的逆元,即Mi * ti ≡ 1 (mod mi)
则 x = ∑ai * Mi * ti(1 <= i <= n)

int main()
{
    cin >> n;
    for(int i = 0; i < n; i ++)
    {
        cin >> m[i] >> a[i];
        M *= m[i];
    }
    LL res = 0;
    for(int i = 0; i < n; i ++)
    {
        LL Mi = M / m[i], ti, y;
        exgcd(Mi, m[i], ti, y);
        res += Mi * ti * a[i];
    }
    cout << (res % M + M) % M << endl;
}


高斯消元

int solve()
{
    int c = 0, r = 0;
    for(; c < n; c ++)
    {
        int t = r;
        // 取当前列最大值,避免取到过小的数
        // 如果取到较小的数在归一时可能会溢出
        for(int i = t + 1; i < n; i ++)
            if(fabs(a[i][c]) > fabs(a[t][c]) )
                t = i;
        
        if(fabs(a[t][c]) < 1e-8) continue;
        
        // 交换两行
        for(int i = 0; i < n + 1; i ++) swap(a[t][i], a[r][i]);
        // 归一
        for(int i = n; i >= c; i --) a[r][i] /= a[r][c];
        
        for(int i = r + 1; i < n; i ++)
            for(int j = n; j >= c; j --)
                a[i][j] -= a[r][j] * a[i][c];
        r ++;
    }
    if(r < n)
    {
        for(int i = r; i < n; i ++)
            if(fabs(a[i][n]) > 1e-8)
                return 2;  // 无解
        return 1;  // 无穷多个解
    }
    // 求解
    for(int i = n - 1; i >= 0; i --)
        for(int j = i - 1; j >= 0; j --)
        {
            a[j][n] -= a[j][i] * a[i][n];
            a[j][i] = 0;
        }
    
    return 0;
}

组合数

杨辉三角形求组合数

// 适用情况:
// 10^7的询问次数 10^3的数据范围
void init()
{
    for(int i = 0; i < N; i ++)
        for(int j = 0; j <= i; j ++)
        {
            if(!j) C[i][j] = 1;
            else C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % MOD;
        }
}

逆元求组合数

// 由费马小定理求逆元
// 适用情况:
// MOD为素数 10^6的数据范围 10^6的询问次数
void init()
{
    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;
    }
}

int C(int a, int b)
{
    return (LL)fact[a] * infact[b] % MOD * infact[a - b] % MOD;
}

lucas定理求组合数

int C(int a, int b, int p)
{
    int res = 1;
    for(int i = a, j = 1; j <= b; i --, j ++)
        res = (LL) res * i % p * qmi(j, p - 2, p) % p;
    return res;
}
// 适用数据范围为10^18,C(a, b)的计算方法可以根据题意选择
LL lucas(LL a, LL b, int p)
{
    if(a < p && b < p) return C(a, b, p);
    return C(a % p, b % p, p) * lucas(a / p, b / p, p) % p;
}

高精度求组合数

// 适用情况:题目要求使用高精度
int get_factor(int a, int p)
{
    int res = 0;
    while(a)
    {
        res += a / p;
        a /= p;
    }
    return res;
}

vector<int> C(int a, int b)
{
    for(int i = 0; i < cnt; i ++)
    {
        int p = primes[i];
        cnts[i] = get_factor(a, p) - get_factor(b, p) - get_factor(a - b, p);
    }
    vector<int> res;
    res.push_back(1);
    
    for(int i = 0; i < cnt; i ++)
        while(cnts[i])
        {
            res = mul(res, primes[i]);
            cnts[i] --;
        }
    
    return res;
}

卡特兰数

void solve()
{
    int a = 2 * n, b = n;
    int res = C(a, b) / (b + 1);
}

容斥原理


博弈论

异或

sg

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值