高精度计算——基础模板整理

高精度计算------基础模板整理

曾经在高精度计算题目合集-CSDN博客 做过类似的工作。但后来觉得这个实现方式不够整洁,有的时候为图方便,直接在数组上多加一维就算是高精度计算。

所以这里尝试将模板重新整理,使得各个高精度计算模板相对更简洁。

当然,不包括浮点数计算、FFT等进阶内容。也许以后会有。

存储高精度大整数

高精度计算本质就是模拟,用数组存储大整数,模拟四则运算的过程。

例如大整数 114514 的各种存储姿势:

  1. 下标表示当前位置的数是第几位。求出结果后还得找到最高位在哪。

存储的数 1 1 4 5 1 4 下标 0 1 2 3 4 5 \begin{matrix}\text{存储的数}&1&1&4&5&1&4\\\text{下标}&0&1&2&3&4&5\end{matrix} 存储的数下标101142531445

  1. 下标表示当前位置的数是第几位, 0 位存储当前整数是几位数。

存储的数 6 1 1 4 5 1 4 下标 0 1 2 3 4 5 6 \begin{matrix}\text{存储的数}&6&1&1&4&5&1&4\\\text{下标}&0&1&2&3&4&5&6\end{matrix} 存储的数下标60111243541546

  1. 10000 进制数,进制可更改,只要保证相乘时不溢出即可。例如使用 int 的话,最适合的进制数是 10 4 10^4 104 , long long 是 10 9 10^9 109 ,__int128 是 10 19 10^{19} 1019 。当然也可用高精度存储更高的进制位,但运算速度会变慢,反而得不偿失。

存储的数 11 4514 2 下标 2 1 0 \begin{matrix}\text{存储的数}&11&4514&2\\\text{下标} &2&1&0\end{matrix} 存储的数下标1124514120

高精度加法 P1601 洛谷

P1601 A+B Problem(高精) - 洛谷

原理如下:

a 5 a 4 a 3 a 2 a 1 + b 4 b 3 b 2 b 1 c 5 c 4 c 3 c 2 c 1 \begin{array}{c}&a_5&a_4&a_3&a_2&a_1\\+&&b_4&b_3&b_2&b_1\\\hline&c_5&c_4&c_3&c_2&c_1\end{array} +a5c5a4b4c4a3b3c3a2b2c2a1b1c1

c i + 1 = c i + 1 + ( c i + a i + b i ) 10 c_{i+1}=c_{i+1}+\frac{(c_i+a_i+b_i)}{ 10} ci+1=ci+1+10(ci+ai+bi) ,

c i = ( c i + ( a i + b i ) )   m o d   10 c_{i}=(c_{i}+(a_i+b_i))\bmod 10 ci=(ci+(ai+bi))mod10 ,

存储方式1参考

cpp 复制代码
#include <bits/stdc++.h>
#include <unordered_map>
using namespace std;

string st1, st2;
int a[510], b[510];

void add(int a[], int b[]) {
    int Max = max(st1.size(), st2.size());
    for (int i = 0; i <= Max; i++) {
        a[i] += b[i];
        a[i + 1] += a[i] / 10;
        a[i] %= 10;
    }
}

int main() {
    // freopen("in.in", "r", stdin);
    cin >> st1 >> st2;
    for (int i = 0; i < st1.size(); i++)
        a[st1.size() - 1 - i] = st1[i] - '0';
    for (int i = 0; i < st2.size(); i++)
        b[st2.size() - 1 - i] = st2[i] - '0';
    add(a, b);
    int p = 509;
    while (p > 0 && a[p] == 0)
        --p;
    while (p >= 0)
        cout << a[p--];
    return 0;
}

存储方式2参考

P1601 A+B Problem(高精) - 洛谷

例如 114514的存储:

数本身 1 1 4 5 1 4 数组存储 6 5 4 3 2 1 6 下标 6 5 4 3 2 1 0 \begin{matrix}\text{数本身}&1&1&4&5&1&4\\\text{数组存储}&6&5&4&3&2&1&6\\\text{下标}&6&5&4&3&2&1&0\end{matrix} 数本身数组存储下标16615544453312241160

参考:

cpp 复制代码
#include <bits/stdc++.h>
#include <unordered_map>
using namespace std;

string st1, st2;
int a[510] = {1}, b[510] = {1};

void add(int a[], int b[]) {
    int Max = max(a[0], b[0]);
    for (int i = 1; i <= Max; i++) {
        a[i] += b[i];
        a[i + 1] += a[i] / 10;
        a[i] %= 10;
    }
    a[0] = Max;
    while (a[a[0] + 1] > 0)
        ++a[0];
}

int main() {
    // freopen("in.in", "r", stdin);
    cin >> st1 >> st2;
    a[0] = st1.size(), b[0] = st2.size();
    for (int i = 0; i < a[0]; i++)
        a[a[0] - i] = st1[i] - '0';
    for (int i = 0; i < b[0]; i++)
        b[b[0] - i] = st2[i] - '0';
    add(a, b);
    for (int i = a[0]; i; i--)
        cout << a[i];
    return 0;
}

存储方式3参考

可以将大整数这样划分,例如 1,2345,0789,1234,5678:

该索引的空间存储的数据 ⏟ 下标 : 1 ⏟ 5 2345 ⏟ 4 0789 ⏟ 3 1234 ⏟ 2 5678 ⏟ 1 \underbrace{该索引的空间存储的数据}{下标}:\underbrace{1}{5}\ \underbrace{2345}{4}\ \underbrace{0789}{3}\ \underbrace{1234}{2}\ \underbrace{5678}{1} 下标 该索引的空间存储的数据:5 1 4 2345 3 0789 2 1234 1 5678

这里 w = log ⁡ 10 b a s e , b a s e = 10000 w=\log_{10}base,base=10000 w=log10base,base=10000 。

当输入 1,2345,0789,1234,5678 存储在 string 对象时,此时将每个数存储在下标为 ⌈ l e n − i w ⌉ \lceil \frac{len-i}{w}\rceil ⌈wlen−i⌉ 的其他数组。例如 i=0 处的 1 , (len-i+w-1)/w=(17-0+4-1)/4=5

整个存储方式 3 的读取数据和输出数据的方式如下:

cpp 复制代码
#include <bits/stdc++.h>
#include <unordered_map>
using namespace std;

int a[510] = {1}, b[510] = {1};
int base = 10000; // 将base设置在外可宏观调控

void read(int a[]) {
    string st;
    cin >> st;
    int w = log10(base), len = st.size();
    for (int i = 0; i < len; i++) {
        int j = (len - i + w - 1) / w;
        a[j] = a[j] * 10 + st[i] - '0';
    }
    a[0] = (len + w - 1) / w;
}

void print(int a[]) {
    int w = log10(base);
    cout << a[a[0]];
    for (int i = a[0] - 1; i; i--)
        cout << setw(w) << setfill('0') << a[i];
}

void add(int a[], int b[]) {
    int Max = max(a[0], b[0]);
    for (int i = 1; i <= Max; i++) {
        a[i] += b[i];
        a[i + 1] += a[i] / base;
        a[i] %= base;
    }
    a[0] = Max;
    while (a[a[0] + 1] > 0)
        a[0]++;
}

int main() {
    // freopen("in.in", "r", stdin);
    read(a), read(b);
    add(a, b);
    print(a);
    return 0;
}

理论上,int 能存储的最高数据量是 10 9 10^9 109 级别的,所以使用 b a s e = 10 4 base=10^4 base=104 就能够支持 2 数相乘不溢出,且这个存储方式能增高空间利用率。

高精度减法 P2142 洛谷

P2142 高精度减法 - 洛谷

存储方式1参考

原理如下:

a 5 a 4 a 3 a 2 a 1 − b 4 b 3 b 2 b 1 c 5 c 4 c 3 c 2 c 1 \begin{array}{c}&a_5&a_4&a_3&a_2&a_1\\-&&b_4&b_3&b_2&b_1\\\hline&c_5&c_4&c_3&c_2&c_1\end{array} −a5c5a4b4c4a3b3c3a2b2c2a1b1c1

c i = a i − b i c_i=a_i-b_i ci=ai−bi ,若 a i < b i a_i<b_i ai<bi 则 a i + 1 = a i + 1 − 1 , a i = a i + 10 a_{i+1}=a_{i+1}-1,a_i=a_i+10 ai+1=ai+1−1,ai=ai+10 。

正整数判断大小:长度不等时谁长谁打,否则从最高位开始比较。

存储方式1参考:

cpp 复制代码
#include <bits/stdc++.h>
#include <unordered_map>
using namespace std;

string st1, st2;
int a[10100], b[10100];

void sub(int a[], int b[]) {
    // 默认a>b,进入函数前需要处理,否则会出错
    int Max = max(st1.size(), st2.size());
    for (int i = 0; i <= Max; i++) {
        if (a[i] < b[i]) {
            a[i + 1] -= 1;
            a[i] += 10;
        }
        a[i] -= b[i];
    }
}

int main() {
    // freopen("in.in", "r", stdin);
    cin >> st1 >> st2;
    bool flag = 0;
    // 正整数判断大小
    if ((st1 < st2 && st1.size() == st2.size()) || (st1.size() < st2.size())) {
        swap(st1, st2);
        flag = 1;
    }
    for (int i = 0; i < st1.size(); i++)
        a[st1.size() - 1 - i] = st1[i] - '0';
    for (int i = 0; i < st2.size(); i++)
        b[st2.size() - 1 - i] = st2[i] - '0';
    sub(a, b);
    int p = 10099;
    while (p > 0 && a[p] == 0)
        --p;
    if (flag)
        putchar('-');
    while (p >= 0)
        cout << a[p--];
    return 0;
}

存储方式2参考

每次减完后都要找到最高位在哪。

cpp 复制代码
#include <bits/stdc++.h>
#include <unordered_map>
using namespace std;

string st1, st2;
int a[10100], b[10100];

void sub(int a[], int b[]) {
    for (int i = 1; i <= a[0]; i++) {
        if (a[i] < b[i]) {
            a[i + 1] -= 1;
            a[i] += 10;
        }
        a[i] -= b[i];
    }
    while (a[0] > 1 && a[a[0]] == 0)
        a[0]--;
}

int main() {
    // freopen("in.in", "r", stdin);
    cin >> st1 >> st2;
    bool flag = 0;
    // 正整数判断大小
    if ((st1 < st2 && st1.size() == st2.size()) || (st1.size() < st2.size())) {
        swap(st1, st2);
        flag = 1;
    }
    a[0] = st1.size(), b[0] = st2.size();
    for (int i = 0; i < st1.size(); i++)
        a[st1.size() - i] = st1[i] - '0';
    for (int i = 0; i < st2.size(); i++)
        b[st2.size() - i] = st2[i] - '0';
    sub(a, b);
    if (flag)
        putchar('-');
    for (int i = a[0]; i; i--)
        cout << a[i];
    return 0;
}

存储方式3参考

每次减完后都要找到最高位在哪,和存储方式 2 的区别就是,10换成了 b a s e base base 。

cpp 复制代码
#include <bits/stdc++.h>
#include <unordered_map>
using namespace std;

string st1, st2;
int a[3010], b[3010];
const int base = 1e4;

void read(int a[], string &st) {
    int w = log10(base), len = st.size();
    for (int i = 0; i < len; i++) {
        int j = (len - i + w - 1) / w;
        a[j] = a[j] * 10 + st[i] - '0';
    }
    a[0] = (len + w - 1) / w;
}

void print(int a[]) {
    cout << a[a[0]];
    int w = log10(base);
    for (int i = a[0] - 1; i; i--)
        cout << setw(w) << setfill('0') << a[i];
}

void sub(int a[], int b[]) {
    for (int i = 1; i <= a[0]; i++) {
        if (a[i] < b[i]) {
            a[i + 1] -= 1;
            a[i] += base;
        }
        a[i] -= b[i];
    }
    while (a[0] > 1 && a[a[0]] == 0)
        a[0]--;
}

int main() {
    // freopen("in.in", "r", stdin);
    cin >> st1 >> st2;
    // 正整数判断大小
    bool flag = 0;
    if ((st1 < st2 && st1.size() == st2.size()) || (st1.size() < st2.size())) {
        swap(st1, st2);
        flag = 1;
    }
    read(a, st1), read(b, st2);
    sub(a, b);
    if (flag)
        putchar('-');
    print(a);
    return 0;
}

高精度大整数乘低精度整数 1170 ybt

1170:计算2的N次方

原本想找一个高精度数乘第低精度数的 OJ ,但找不到,只能用这个充当。

原理还是小学的竖式乘法。

a 5 a 4 a 3 a 2 a 1 × b c 6 c 5 c 4 c 3 c 2 c 1 \begin{array}{c}&&a_5&a_4&a_3&a_2&a_1\\\times&&& & & &b\\\hline &c_6&c_5&c_4&c_3&c_2&c_1\end{array} ×c6a5c5a4c4a3c3a2c2a1bc1

但不同的是,这个 b b b 可以很大,只要相乘和相加过程中不超过变量的精度即可。

存储方式1参考

进位时需要使用额外的 w 存储。

cpp 复制代码
#include <bits/stdc++.h>
#include <unordered_map>
using namespace std;

int a[101] = {1};

void mul(int a[], int b) {
    int w = 0;
    for (int i = 0; i < 100; i++) {
        a[i] = a[i] * b + w;
        w = a[i] / 10;
        a[i] %= 10;
    }
    // 这个存储方式不关心最高位到哪了
}

int main() {
    // freopen("in.in", "r", stdin);
    int N, b = 2;
    cin >> N;
    while (N--)
        mul(a, b);
    int p = 100;
    while (p && a[p] == 0)
        p--;
    while (p >= 0)
        cout << a[p--];

    return 0;
}

存储方式2参考

每次减完后都要找到最高位在哪。

cpp 复制代码
#include <bits/stdc++.h>
#include <unordered_map>
using namespace std;

int a[101] = {1, 1};

void mul(int a[], int b) {
    int w = 0;
    for (int i = 1; i <= a[0]; i++) {
        a[i] = a[i] * b + w;
        w = a[i] / 10;
        a[i] %= 10;
    }
    while (w) {
        a[++a[0]] += w % 10;
        w /= 10;
    }
    // 可能会出现乘0的情况
    while (a[0] > 1 && a[a[0]] == 0)
        a[0]--;
}

int main() {
    // freopen("in.in", "r", stdin);
    int N, b = 2;
    cin >> N;
    while (N--)
        mul(a, b);
    for (int i = a[0]; i; i--)
        cout << a[i];
    return 0;
}

存储方式3参考

每次减完后都要找到最高位在哪。

cpp 复制代码
#include <bits/stdc++.h>
#include <unordered_map>
using namespace std;

int a[101] = {1, 1};
const int base = 10000;

void mul(int a[], int b) {
    int w = 0;
    for (int i = 1; i <= a[0]; i++) {
        a[i] = a[i] * b + w;
        w = a[i] / base;
        a[i] %= base;
    }
    while (w) {
        a[++a[0]] += w % base;
        w /= base;
    }
    // 可能会出现乘0的情况
    while (a[0] > 1 && a[a[0]] == 0)
        a[0]--;
}

void print(int a[]) {
    cout << a[a[0]];
    int w = log10(base);
    for (int i = a[0] - 1; i; i--)
        cout << setw(w) << setfill('0') << a[i];
}

int main() {
    // freopen("in.in", "r", stdin);
    int N, b = 2;
    cin >> N;
    while (N--)
        mul(a, b);
    print(a);
    return 0;
}

万位内的高精度乘法 P1303 洛谷

P1303 A*B Problem - 洛谷

无论是哪一种存储方式,计算的时间复杂度都是 O ( n 2 ) \text{O}(n^2) O(n2) ,最多可处理 ≤ 10 10000 \le 10^{10000} ≤1010000 的数据,否则会超时。数据量一旦超过这个值,就需要使用分治乘法或快速傅里叶变换,后期有机会再单独开一篇。

原理:

a 4 a 3 a 2 a 1 a 0 × b 3 b 2 b 1 b 0 c 8 c 7 c 6 c 5 c 4 c 3 c 2 c 1 c 0 \begin{array}{c}&&&&&a_4&a_3&a_2&a_1&a_0\\\times&&&&&&b_3&b_2&b_1&b_0\\\hline&c_8&c_7&c_6&c_5&c_4&c_3&c_2&c_1&c_0\end{array} ×c8c7c6c5a4c4a3b3c3a2b2c2a1b1c1a0b0c0

例如:

9 9 9 9 9 × 9 9 9 9 8 9 9 9 9 1 8 9 9 9 9 1 8 9 9 9 9 1 8 9 9 9 9 1 9 9 9 8 9 0 0 0 1 \begin{array}{c}&&&&&9&9&9&9&9\\\times&&&&&&9&9&9&9\\\hline&&&&8&9&9&9&9&1\\&&&8&9&9&9&9&1\\&&8&9&9&9&9&1\\&8&9&9&9&9&1\\\hline&9&9&9&8&9&0&0&0&1\end{array} ×898998999899989999999999910999910999109911

所以 c i + j = c i + j + a i × b j + w c_{i+j}=c_{i+j}+a_i\times b_j+w ci+j=ci+j+ai×bj+w , w = ⌊ c i + j 10 ⌋ w=\lfloor\frac{c_{i+j}}{10}\rfloor w=⌊10ci+j⌋ 。

注意不要忘 c i + j   m o d   10 c_{i+j}\bmod 10 ci+jmod10 。

存储方式1参考

数组下标表示第几位数。

cpp 复制代码
#include <bits/stdc++.h>
#include <unordered_map>
using namespace std;

string st1, st2;
int a[2010], b[2010], c[4020];

void mul(int a[], int b[], int c[]) {
    int w, j;
    for (int i = 0; i < st1.size(); i++) {
        w = 0;
        for (j = 0; j < st2.size(); j++) {
            c[i + j] = c[i + j] + a[i] * b[j] + w;
            w = c[i + j] / 10;
            c[i + j] %= 10;
        }
        while (w) {
            c[i + j] += w;
            w /= 10;
        }
    }
}

int main() {
    // freopen("in.in", "r", stdin);
    cin >> st1 >> st2;
    for (int i = 0; i < st1.size(); i++)
        a[st1.size() - 1 - i] = st1[i] - '0';
    for (int i = 0; i < st2.size(); i++)
        b[st2.size() - 1 - i] = st2[i] - '0';
    mul(a, b, c);
    int p = 4019;
    while (p > 0 && c[p] == 0)
        p--;
    while (p >= 0)
        cout << c[p--];
    return 0;
}

存储方式2参考

因为下标从 1 开始,所以需要重新分析。

a 5 a 4 a 3 a 2 a 1 × b 4 b 3 b 2 b 1 c 9 c 8 c 7 c 6 c 5 c 4 c 3 c 2 c 1 \begin{array}{c}&&&&&a_5&a_4&a_3&a_2&a_1\\\times&&&&&&b_4&b_3&b_2&b_1\\\hline&c_9&c_8&c_7&c_6&c_5&c_4&c_3&c_2&c_1\end{array} ×c9c8c7c6a5c5a4b4c4a3b3c3a2b2c2a1b1c1

所以 c i + j − 1 = c i + j − 1 + a i × b j + w c_{i+j-1}=c_{i+j-1}+a_i\times b_j+w ci+j−1=ci+j−1+ai×bj+w , w = ⌊ c i + j − 1 10 ⌋ w=\lfloor\frac{c_{i+j-1}}{10}\rfloor w=⌊10ci+j−1⌋ 。

注意不要忘 c i + j − 1   m o d   10 c_{i+j-1}\bmod 10 ci+j−1mod10 。

cpp 复制代码
#include <bits/stdc++.h>
#include <unordered_map>
using namespace std;

string st1, st2;
int a[2010] = {1, 0}, b[2010] = {1, 0}, c[4020] = {1, 0};

void mul(int a[], int b[], int c[]) {
    int w, j;
    c[0] = a[0] + b[0];
    for (int i = 1; i <= a[0]; i++) {
        w = 0;
        for (j = 1; j <= b[0]; j++) {
            c[i + j - 1] = c[i + j - 1] + a[i] * b[j] + w;
            w = c[i + j - 1] / 10;
            c[i + j - 1] %= 10;
        }
        while (w) {
            c[i + j - 1] += w;
            w /= 10;
        }
    }
    while (c[0] > 1 && c[c[0]] == 0)
        c[0]--;
}

int main() {
    // freopen("in.in", "r", stdin);
    cin >> st1 >> st2;
    a[0] = st1.size(), b[0] = st2.size();
    for (int i = 0; i < st1.size(); i++)
        a[st1.size() - i] = st1[i] - '0';
    for (int i = 0; i < st2.size(); i++)
        b[st2.size() - i] = st2[i] - '0';
    mul(a, b, c);
    for (int i = c[0]; i; i--)
        cout << c[i];
    return 0;
}

存储方式3参考

这种存储方式因为使用了 b a s e base base 进制存储原理,理论上最多能处理 ≤ 10 log ⁡ 10 b a s e × 10000 \le 10^{\log_{10}base\times10000} ≤10log10base×10000 的数据,但实际需要考虑计算机性能等原因。

例如这里 b a s e = 10000 base=10000 base=10000 ,则理论上最多能处理 10 40000 10^{40000} 1040000 位整数的乘法,但实际受到设备等的限制。

cpp 复制代码
#include <bits/stdc++.h>
#include <unordered_map>
using namespace std;

string st1, st2;
int a[2010] = {1, 0}, b[2010] = {1, 0}, c[4020] = {1, 0};
int base = 10000;

void mul(int a[], int b[], int c[]) {
    int w, j;
    c[0] = a[0] + b[0];
    for (int i = 1; i <= a[0]; i++) {
        w = 0;
        for (j = 1; j <= b[0]; j++) {
            c[i + j - 1] = c[i + j - 1] + a[i] * b[j] + w;
            w = c[i + j - 1] / base;
            c[i + j - 1] %= base;
        }
        while (w) {
            c[i + j - 1] += w;
            w /= base;
        }
    }
    while (c[0] > 1 && c[c[0]] == 0)
        c[0]--;
}

void read(int a[]) {
    string st;
    cin >> st;
    int w = log10(base), len = st.size();
    for (int i = 0; i < len; i++) {
        int j = (len - i + w - 1) / w;
        a[j] = a[j] * 10 + st[i] - '0';
    }
    a[0] = (len + w - 1) / w;
}

void print(int a[]) {
    cout << a[a[0]];
    int w = log10(base);
    for (int i = a[0] - 1; i; i--)
        cout << setw(w) << setfill('0') << a[i];
}

int main() {
    // freopen("in.in", "r", stdin);
    read(a), read(b);
    mul(a, b, c);
    print(c);
    return 0;
}

高精度大整数除以低精度整数 P1480 洛谷

P1480 A/B Problem(高精度除法Ⅰ) - 洛谷

存储方式1参考

原理:例如竖式除法 114514 ÷ 74 = 1547 ... 36 114514\div 74=1547\dots36 114514÷74=1547...36:

不停枚举被除数的高数位,将所有除法和取模的值进行统计,再翻转,去除前缀的 0 即可。

参考程序:

cpp 复制代码
#include <bits/stdc++.h>
#include <unordered_map>
using namespace std;

string st;
int a[5010], b, c[5010];

void div(int a[], int b, int c[]) {
    int ip = 0;
    long long num = 0;
    for (int i = st.size() - 1; i >= 0; i--) {
        num = num * 10 + a[i];
        c[ip++] = num / b;
        num %= b;
    }
    reverse(c, c + ip);
}

int main() {
    // freopen("in.in", "r", stdin);
    cin >> st >> b;
    for (int i = 0; i < st.size(); i++)
        a[st.size() - 1 - i] = st[i] - '0';
    div(a, b, c);
    int p = 5009;
    while (p && c[p] == 0)
        p--;
    while (p >= 0)
        cout << c[p--];
    return 0;
}

存储方式2参考

cpp 复制代码
#include <bits/stdc++.h>
#include <unordered_map>
using namespace std;

string st;
int a[5010], b, c[5010];

void div(int a[], int b, int c[]) {
    int ip = 0;
    long long num = 0;
    for (int i = a[0]; i; i--) {
        num = num * 10 + a[i];
        c[++ip] = num / b;
        num %= b;
    }
    reverse(c + 1, c + ip + 1);
    c[0] = ip;
    while (c[0] > 1 && c[c[0]] == 0)
        --c[0];
}

int main() {
    // freopen("in.in", "r", stdin);
    cin >> st >> b;
    a[0] = st.size();
    for (int i = 0; i < st.size(); i++)
        a[st.size() - i] = st[i] - '0';
    div(a, b, c);
    for (int i = c[0]; i; i--)
        cout << c[i];
    return 0;
}

存储方式3参考

存储方式 2 的除法部分,10 换成 b a s e base base 即可。

cpp 复制代码
#include <bits/stdc++.h>
#include <unordered_map>
using namespace std;

int a[5010], b, c[5010];
const int base = 10000;

void read(int a[]) {
    string st;
    cin >> st;
    int w = log10(base + base / 2), len = st.size();
    for (int i = 0; i < len; i++) {
        int j = (len - i + w - 1) / w;
        a[j] = a[j] * 10 + st[i] - '0';
    }
    a[0] = (len + w - 1) / w;
}

void print(int a[]) {
    cout << a[a[0]];
    int w = log10(base);
    for (int i = a[0] - 1; i; i--)
        cout << setw(w) << setfill('0') << a[i];
}

void div(int a[], int b, int c[]) {
    int ip = 0;
    long long num = 0;
    for (int i = a[0]; i; i--) {
        num = num * base + a[i];
        c[++ip] = num / b;
        num %= b;
    }
    reverse(c + 1, c + ip + 1);
    c[0] = ip;
    while (c[0] > 1 && c[c[0]] == 0)
        --c[0];
}

int main() {
    // freopen("in.in", "r", stdin);
    read(a), cin >> b;
    div(a, b, c);
    print(c);
    return 0;
}

高精度除法 P2005 洛谷

P2005 A/B Problem II - 洛谷

数据量最大可达 6250 ! 6250! 6250! ,根据高精度阶乘算法的结果,是 10 21014 10^{21014} 1021014 数量级。

也就是说,这个题需要频繁进行减法。

存储方式2参考

因为 2 个都是高精度计算,涉及多次比较,使用存储方式 1 不方便,所以使用存储方式 2 。

除了减法,还用到了高精度大整数乘低精度整数和自定义比较函数。

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
using vi = vector<int>;

string st1, st2;
vi a(21100), b(21100), c(21100);
// 减法
void sub(vi &a, vi &b) {
    for (int i = 1; i <= a[0]; i++) {
        if (a[i] < b[i]) {
            a[i + 1] -= 1;
            a[i] += 10;
        }
        a[i] -= b[i];
    }
    while (a[0] > 1 && a[a[0]] == 0)
        a[0]--;
}

// 低精度乘
void mul(vi &a, int b) {
    int w = 0;
    for (int i = 1; i <= a[0]; i++) {
        a[i] = a[i] * b + w;
        w = a[i] / 10;
        a[i] %= 10;
    }
    while (w) {
        a[++a[0]] += w % 10;
        w /= 10;
    }
}

// 大于等于
bool ge(vi &a, vi &b) {
    if (a[0] > b[0])
        return 1;
    if (a[0] == b[0]) {
        for (int i = a[0]; i; i--)
            if (a[i] != b[i])
                return a[i] >= b[i];
        return 1;
    }
    return 0;
}

void hdiv(vi &a, vi &b, vi &c) {
    int ip = 0, cnt = 0;
    vi num(21100, 0);
    num[0] = 1;
    for (int i = a[0]; i; i--) {
        mul(num, 10);
        num[1] += a[i];
        cnt = 0;
        while (ge(num, b)) {
            ++cnt;
            sub(num, b);
        }
        c[++ip] = cnt;
    }
    c[0] = ip;
    // 多了翻转,所以多费点时间
    reverse(c.begin() + 1, c.begin() + ip + 1);
    while (c[0] > 1 && c[c[0]] == 0)
        --c[0];
}

int main() {
    // freopen("in.in", "r", stdin);
    cin >> st1 >> st2;
    a[0] = st1.size(), b[0] = st2.size();
    for (int i = 0, len = st1.size(); i < len; i++)
        a[len - i] = st1[i] - '0';
    for (int i = 0, len = st2.size(); i < len; i++)
        b[len - i] = st2[i] - '0';
    hdiv(a, b, c);
    for (int i = c[0]; i; i--)
        cout << c[i];
    return 0;
}

存储方式3参考

因为使用了 b a s e base base 进制,所以减法部分需要枚举 [1,base-1]base 越大枚举成本越大,所以使用二分优化枚举。但要注意的是,二分结束后还要将找到的结果重新乘一遍,否则会出现负数导致结果出错。

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
using vi = vector<int>;

const int base = 10000;
string st1, st2;
vi a(5500), b(5500), c(5500);

void read(vi &a) {
    string st;
    cin >> st;
    int w = log10(base + base / 2), len = st.size();
    for (int i = 0; i < len; i++) {
        int j = (len - i + w - 1) / w;
        a[j] = a[j] * 10 + st[i] - '0';
    }
    a[0] = (len + w - 1) / w;
}

void print(vi &a) {
    cout << a[a[0]];
    int w = log10(base + base / 2);
    for (int i = a[0] - 1; i; i--)
        cout << setw(w) << setfill('0') << a[i];
}

// 减法
void sub(vi &a, vi &b) {
    for (int i = 1; i <= a[0]; i++) {
        if (a[i] < b[i]) {
            a[i + 1] -= 1;
            a[i] += base;
        }
        a[i] -= b[i];
    }
    while (a[0] > 1 && a[a[0]] == 0)
        a[0]--;
}

// 低精度乘
void mul(vi &a, int b) {
    int w = 0;
    for (int i = 1; i <= a[0]; i++) {
        a[i] = a[i] * b + w;
        w = a[i] / base;
        a[i] %= base;
    }
    while (w) {
        a[++a[0]] += w % base;
        w /= base;
    }
}

// 大于等于
bool ge(vi &a, vi &b) {
    if (a[0] > b[0])
        return 1;
    if (a[0] == b[0]) {
        for (int i = a[0]; i; i--)
            if (a[i] != b[i])
                return a[i] >= b[i];
        return 1;
    }
    return 0;
}

// 高精度除
void hdiv(vi &a, vi &b, vi &c) {
    int ip = 0, cnt = 0;
    vi num(5500, 0);
    num[0] = 1;
    vi tmp = b;
    for (int i = a[0]; i; i--) {
        mul(num, base);
        num[1] += a[i];
        cnt = 0;
        if (ge(num, b)) {
            // 二分优化枚举
            int L = 1, R = base - 1;
            while (L < R) {
                int mid = (L + R + 1) / 2;
                tmp = b; // 使用vector的好处体现在这些地方
                mul(tmp, mid);
                if (ge(tmp, num))
                    R = mid - 1;
                else
                    L = mid;
            }
            cnt = L;
            tmp = b; // 二分结束后记得将结果还原
            mul(tmp, L);
            sub(num, tmp);
        }
        c[++ip] = cnt;
    }
    c[0] = ip;
    // 多了翻转,所以多费点时间
    reverse(c.begin() + 1, c.begin() + ip + 1);
    while (c[0] > 1 && c[c[0]] == 0)
        --c[0];
}

int main() {
    // freopen("in.in", "r", stdin);
    read(a), read(b);
    hdiv(a, b, c);
    print(c);
    return 0;
}

高精度取模 P1932 洛谷

P1932 A+B A-B A*B A/B A%B Problem - 洛谷

上文的所有题目缝合在一起即可。即使是取模,也只需要在原来的除法的基础上将 num 数组返回即可。

没有找到取模的 OJ ,就用这个代替,反正都差不多。

存储方式2参考

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
using vi = vector<int>;

string st1, st2;

void add(vi& a, vi& b) {
    int Max = max(a[0], b[0]);
    for (int i = 1; i <= Max; i++) {
        a[i] += b[i];
        a[i + 1] += a[i] / 10;
        a[i] %= 10;
    }
    a[0] = Max;
    while (a[a[0] + 1] > 0)
        ++a[0];
}

void sub(vi& a, vi& b) {
    for (int i = 1; i <= a[0]; i++) {
        if (a[i] < b[i]) {
            a[i + 1] -= 1;
            a[i] += 10;
        }
        a[i] -= b[i];
    }
    while (a[0] > 1 && a[a[0]] == 0)
        a[0]--;
}

void mul(vi& a, int b) {
    int w = 0;
    for (int i = 1; i <= a[0]; i++) {
        a[i] = a[i] * b + w;
        w = a[i] / 10;
        a[i] %= 10;
    }
    while (w) {
        a[++a[0]] += w % 10;
        w /= 10;
    }
}

void mul(vi& a, vi& b, vi& c) {
    int w, j;
    c[0] = a[0] + b[0];
    for (int i = 1; i <= a[0]; i++) {
        w = 0;
        for (j = 1; j <= b[0]; j++) {
            c[i + j - 1] = c[i + j - 1] + a[i] * b[j] + w;
            w = c[i + j - 1] / 10;
            c[i + j - 1] %= 10;
        }
        while (w) {
            c[i + j - 1] += w;
            w /= 10;
        }
    }
    while (c[0] > 1 && c[c[0]] == 0)
        c[0]--;
}

bool ge(vi& a, vi& b) {
    if (a[0] > b[0])
        return 1;
    if (a[0] == b[0]) {
        for (int i = a[0]; i; i--)
            if (a[i] != b[i])
                return a[i] >= b[i];
        return 1;
    }
    return 0;
}

vi hdiv(vi& a, vi& b, vi& c) {
    int ip = 0, cnt = 0;
    vi num(2e4 + 10, 0);
    num[0] = 1;
    for (int i = a[0]; i; i--) {
        mul(num, 10);
        num[1] += a[i];
        cnt = 0;
        while (ge(num, b)) {
            ++cnt;
            sub(num, b);
        }
        c[++ip] = cnt;
    }
    c[0] = ip;
    // 多了这个,所以多费点时间
    reverse(c.begin() + 1, c.begin() + ip + 1);
    while (c[0] > 1 && c[c[0]] == 0)
        --c[0];
    return num;
}

void cop(vi&a,vi&b) {
    a[0] = st1.size(), b[0] = st2.size();
    for (int i = 0; i < a[0]; i++)
        a[a[0] - i] = st1[i] - '0';
    for (int i = 0; i < b[0]; i++)
        b[b[0] - i] = st2[i] - '0';
}

void print(vi& a) {
    for (int i = a[0]; i; i--)
        cout << a[i];
    cout << '\n';
}

void ac(int op) {
    vi a(2e4 + 10, 0), b(2e4 + 10, 0);
    vi c(2e4 + 10, 0);
    c[0] = 1;
    cop(a,b);
    switch (op) {
    case 0:
        add(a, b);
        break;
    case 1:
        if (!(ge(a, b))) {
            swap(a, b);
            putchar('-');
        }
        sub(a, b);
        break;
    case 2:
        mul(a, b, c);
        a = c;
        break;
    case 3:
        hdiv(a, b, c);
        a = c;
        break;
    case 4:
        a = hdiv(a, b, c);
        break;
    }
    print(a);
}

int main() {
    //freopen("in.in", "r", stdin);
    cin >> st1 >> st2;
    for (int i = 0; i < 5; i++)
        ac(i);
    return 0;
}

存储方式3参考

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
using vi = vector<int>;

string st1, st2;
int base = 10000; // 将base设置在外可宏观调控

void read(vi &a, string &st) {
    int w = log10(base), len = st.size();
    for (int i = 0; i < len; i++) {
        int j = (len - i + w - 1) / w;
        a[j] = a[j] * 10 + st[i] - '0';
    }
    a[0] = (len + w - 1) / w;
}

void print(vi &a) {
    int w = log10(base);
    cout << a[a[0]];
    for (int i = a[0] - 1; i; i--)
        cout << setw(w) << setfill('0') << a[i];
}

void add(vi &a, vi &b) {
    int Max = max(a[0], b[0]);
    for (int i = 1; i <= Max; i++) {
        a[i] += b[i];
        a[i + 1] += a[i] / base;
        a[i] %= base;
    }
    a[0] = Max;
    while (a[a[0] + 1] > 0)
        a[0]++;
}

void sub(vi &a, vi &b) {
    for (int i = 1; i <= a[0]; i++) {
        if (a[i] < b[i]) {
            a[i + 1] -= 1;
            a[i] += base;
        }
        a[i] -= b[i];
    }
    while (a[0] > 1 && a[a[0]] == 0)
        a[0]--;
}

void mul(vi &a, int b) {
    int w = 0;
    for (int i = 1; i <= a[0]; i++) {
        a[i] = a[i] * b + w;
        w = a[i] / base;
        a[i] %= base;
    }
    while (w) {
        a[++a[0]] += w % base;
        w /= base;
    }
}

void mul(vi &a, vi &b, vi &c) {
    int w, j;
    c[0] = a[0] + b[0];
    for (int i = 1; i <= a[0]; i++) {
        w = 0;
        for (j = 1; j <= b[0]; j++) {
            c[i + j - 1] = c[i + j - 1] + a[i] * b[j] + w;
            w = c[i + j - 1] / base;
            c[i + j - 1] %= base;
        }
        while (w) {
            c[i + j - 1] += w;
            w /= base;
        }
    }
    while (c[0] > 1 && c[c[0]] == 0)
        c[0]--;
}

bool ge(vi &a, vi &b) {
    if (a[0] > b[0])
        return 1;
    if (a[0] == b[0]) {
        for (int i = a[0]; i; i--)
            if (a[i] != b[i])
                return a[i] >= b[i];
        return 1;
    }
    return 0;
}

vi hdiv(vi &a, vi &b, vi &c) {
    int ip = 0, cnt = 0;
    vi num(5100, 0);
    num[0] = 1;
    vi tmp = b;
    for (int i = a[0]; i; i--) {
        mul(num, base);
        num[1] += a[i];
        cnt = 0;
        if (ge(num, b)) {
            // 二分优化枚举
            int L = 1, R = base - 1;
            while (L < R) {
                int mid = (L + R + 1) / 2;
                tmp = b;
                mul(tmp, mid);
                if (ge(tmp, num))
                    R = mid - 1;
                else
                    L = mid;
            }
            cnt = L;
            tmp = b;
            mul(tmp, L);
            sub(num, tmp);
        }
        c[++ip] = cnt;
    }
    c[0] = ip;
    // 多了这个,所以多费点时间
    reverse(c.begin() + 1, c.begin() + ip + 1);
    while (c[0] > 1 && c[c[0]] == 0)
        --c[0];
    return num;
}

void cop(vi &a, vi &b) {
    read(a, st1);
    read(b, st2);
}

void ac(int op) {
    vi a(5100, 0), b(5100, 0);
    vi c(5100, 0);
    c[0] = 1;
    cop(a, b);
    switch (op) {
    case 0:
        add(a, b);
        break;
    case 1:
        if (!(ge(a, b))) {
            swap(a, b);
            putchar('-');
        }
        sub(a, b);
        break;
    case 2:
        mul(a, b, c);
        a = c;
        break;
    case 3:
        hdiv(a, b, c);
        a = c;
        break;
    case 4:
        a = hdiv(a, b, c);
        break;
    }
    print(a);
    putchar('\n');
}

int main() {
    // freopen("in.in", "r", stdin);
    cin >> st1 >> st2;
    for (int i = 0; i < 5; i++)
        ac(i);
    return 0;
}

10000以内n的阶乘 1172 ybt

1172:求10000以内n的阶乘

进行 10000 次高精度乘低精度即可。存储方式不知道该乘到哪里,所以用起来不方便,所以不用存储方式 1 。

存储方式2

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
using vi = vector<int>;

inline void mul(vi &a, int b) {
    int w = 0;
    for (int i = 1; i <= a[0]; i++) {
        a[i] = a[i] * b + w;
        w = a[i] / 10;
        a[i] %= 10;
    }
    while (w) {
        a[++a[0]] += w % 10;
        w /= 10;
    }
}

int main() {
    // freopen("in.in", "r", stdin);
    // 10000!是35560位
    vi a(35600, 0);
    a[0] = a[1] = 1;
    int n;
    cin >> n;
    for (int i = 2; i <= n; i++)
        mul(a, i);
    for (int i = a[0]; i; i--)
        cout << a[i];
    return 0;
}

存储方式3

空间利用率变大,比原来更快。

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
using vi = vector<int>;

const int base = 10000;
inline void mul(vi &a, int b) {
    int w = 0;
    for (int i = 1; i <= a[0]; i++) {
        a[i] = a[i] * b + w;
        w = a[i] / base;
        a[i] %= base;
    }
    while (w) {
        a[++a[0]] += w % base;
        w /= base;
    }
}

void print(vi &a) {
    cout << a[a[0]];
    int w = log10(base + base / 2);
    for (int i = a[0] - 1; i; i--)
        cout << setw(w) << setfill('0') << a[i];
}

int main() {
    // freopen("in.in", "r", stdin);
    // 10000!是35560位
    vi a(9000, 0);
    a[0] = a[1] = 1;
    int n;
    cin >> n;
    for (int i = 2; i <= n; i++)
        mul(a, i);
    print(a);
    return 0;
}

P1009 阶乘之和 - 洛谷

P1009 [NOIP 1998 普及组\] 阶乘之和 - 洛谷](https://www.luogu.com.cn/problem/P1009) [1173:阶乘和](http://ybt.ssoier.cn:8088/problem_show.php?pid=1173) 边求阶乘边求和即可。但题目数据量偏小,不知道为什么。 ### 存储方式2 ```cpp #include using namespace std; using vi = vector; inline void mul(vi &a, int b) { int w = 0; for (int i = 1; i <= a[0]; i++) { a[i] = a[i] * b + w; w = a[i] / 10; a[i] %= 10; } while (w) { a[++a[0]] += w % 10; w /= 10; } } inline void add(vi &a, vi &b) { int Max = max(a[0], b[0]); for (int i = 1; i <= Max; i++) { a[i] += b[i]; a[i + 1] += a[i] / 10; a[i] %= 10; } a[0] = Max; while (a[a[0] + 1] > 0) ++a[0]; } int main() { // freopen("in.in", "r", stdin); // 10000!是35560位 vi a(35600, 0); a[0] = a[1] = 1; vi b = a; int n; cin >> n; for (int i = 2; i <= n; i++) { mul(a, i); add(b, a); } for (int i = b[0]; i; i--) cout << b[i]; return 0; } ``` ### 存储方式3 ```cpp #include using namespace std; using vi = vector; int base = 10000; inline void print(vi &a) { int w = log10(base); cout << a[a[0]]; for (int i = a[0] - 1; i; i--) cout << setw(w) << setfill('0') << a[i]; } inline void mul(vi &a, int b) { int w = 0; for (int i = 1; i <= a[0]; i++) { a[i] = a[i] * b + w; w = a[i] / base; a[i] %= base; } while (w) { a[++a[0]] += w % base; w /= base; } } inline void add(vi &a, vi &b) { int Max = max(a[0], b[0]); for (int i = 1; i <= Max; i++) { a[i] += b[i]; a[i + 1] += a[i] / base; a[i] %= base; } a[0] = Max; while (a[a[0] + 1] > 0) a[0]++; } int main() { // freopen("in.in", "r", stdin); // 10000!是35560位 vi a(9000, 0); a[0] = a[1] = 1; vi b = a; int n; cin >> n; for (int i = 2; i <= n; i++) { mul(a, i); add(b, a); } print(b); return 0; } ``` ## OJ参考 [P1601 A+B Problem(高精) - 洛谷](https://www.luogu.com.cn/problem/P1601) [P2142 高精度减法 - 洛谷](https://www.luogu.com.cn/problem/P2142) [1170:计算2的N次方](http://ybt.ssoier.cn:8088/problem_show.php?pid=1170) [P1303 A\*B Problem - 洛谷](https://www.luogu.com.cn/problem/P1303) [P1480 A/B Problem(高精度除法Ⅰ) - 洛谷](https://www.luogu.com.cn/problem/P1480) [P2005 A/B Problem II - 洛谷](https://www.luogu.com.cn/problem/P2005) [P1932 A+B A-B A\*B A/B A%B Problem - 洛谷](https://www.luogu.com.cn/problem/P1932) [1172:求10000以内n的阶乘](http://ybt.ssoier.cn:8088/problem_show.php?pid=1172) \[P1009 [NOIP 1998 普及组\] 阶乘之和 - 洛谷](https://www.luogu.com.cn/problem/P1009) [1173:阶乘和](http://ybt.ssoier.cn:8088/problem_show.php?pid=1173)

相关推荐
普马萨特2 小时前
基站 / WiFi 粗略位置对 A-GNSS 的影响
网络·人工智能·算法
Tanecious.2 小时前
蓝桥杯备赛:Day5-P1036 选数
c++·蓝桥杯
py有趣2 小时前
力扣热门100题之接雨水
算法·leetcode
mmz12072 小时前
深度优先搜索DFS(c++)
c++·算法·深度优先
汀、人工智能3 小时前
[特殊字符] 第103课:单词搜索II
数据结构·算法·均值算法·前缀树·trie·单词搜索ii
憧憬从前4 小时前
算法学习记录DAY1
c++·学习
A.A呐4 小时前
【C++第二十四章】异常
开发语言·c++
wanderist.4 小时前
算法模板-字符串
数据结构·算法·哈希算法
xiaoye-duck4 小时前
《算法题讲解指南:动态规划算法--子序列问题》--29.最长递增子序列的个数,30.最长数对链,31.最长定差子序列
c++·算法·动态规划