高精度计算------基础模板整理
- 存储高精度大整数
- [高精度加法 P1601 洛谷](#高精度加法 P1601 洛谷)
- [高精度减法 P2142 洛谷](#高精度减法 P2142 洛谷)
- [高精度大整数乘低精度整数 1170 ybt](#高精度大整数乘低精度整数 1170 ybt)
- [万位内的高精度乘法 P1303 洛谷](#万位内的高精度乘法 P1303 洛谷)
- [高精度大整数除以低精度整数 P1480 洛谷](#高精度大整数除以低精度整数 P1480 洛谷)
- [高精度除法 P2005 洛谷](#高精度除法 P2005 洛谷)
- [高精度取模 P1932 洛谷](#高精度取模 P1932 洛谷)
- [10000以内n的阶乘 1172 ybt](#10000以内n的阶乘 1172 ybt)
- [P1009 阶乘之和 - 洛谷](#P1009 阶乘之和 - 洛谷)
- OJ参考
曾经在高精度计算题目合集-CSDN博客 做过类似的工作。但后来觉得这个实现方式不够整洁,有的时候为图方便,直接在数组上多加一维就算是高精度计算。
所以这里尝试将模板重新整理,使得各个高精度计算模板相对更简洁。
当然,不包括浮点数计算、FFT等进阶内容。也许以后会有。
存储高精度大整数
高精度计算本质就是模拟,用数组存储大整数,模拟四则运算的过程。
例如大整数 114514 的各种存储姿势:
- 下标表示当前位置的数是第几位。求出结果后还得找到最高位在哪。
存储的数 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
- 下标表示当前位置的数是第几位, 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
- 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 洛谷
原理如下:
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参考
例如 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 洛谷
存储方式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
原本想找一个高精度数乘第低精度数的 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 洛谷
无论是哪一种存储方式,计算的时间复杂度都是 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 洛谷
数据量最大可达 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
进行 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