高精度除以高精度:手写十进制竖式长除法,逐位拉下被除数、反复减去除数试商,O((n-m+1)m) 求得商与余数。
OJ: roj
题目 ID: 1308
难度:入门
标签:高精度大整数数学
创建: 2026-09-30 04:19
更新: 2026-10-05 08:50
题目描述
给定两个位数不超过 0;样例输入 1231312318457577687897987642324567864324567876543245671425346756786867867867 与 1231312318767141738178325678412414124141425346756786867867867,对应输出 999999999748590 与 179780909068307566598992807564736854549985603543237528310337。
思路
用竖式长除法:从高位到低位依次把 0),余数已保持无前导零的规范表示,直接输出即可。
参考代码
Python 版:
python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-09-30 04:19
# update_at: 2026-09-30 04:37
import sys
def solve() -> None:
"""读入两个高精度正整数,输出它们的商和余数。"""
# 两个操作数各占一行;split() 按空白切分,恰好得到两个 token
a, b = map(int, sys.stdin.buffer.read().split())
# int 是任意精度整数:竖式的逐位试商由解释器完成,同阶于手写长除法
q, r = divmod(a, b)
print(q, r, sep='\n') # 商、余数各占一行
if __name__ == "__main__":
solve()C++ 版(同一算法):
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-10-05 08:49
* update_at: 2026-10-05 08:50
*/
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
const int MAXLEN = 305; // 输入位数上限 300,开到 305 留余量
char str_a[MAXLEN];
char str_b[MAXLEN];
int a[MAXLEN]; // 被除数 A 的数位,a[0] 是最高位
int b[MAXLEN]; // 除数 B 的数位,b[0] 是最高位
int rem[MAXLEN]; // 当前部分被除数(余数),rem[0] 是最高位,无前导零
int q[MAXLEN]; // 商的数位,q[0] 是最高位
ll la; // A 的位数
ll lb; // B 的位数
ll lr; // 当前余数的位数
ll lq = 0; // 商的位数
// 把 rem 与 b 比较:rem > b 返回 1,相等返回 0,小于返回 -1。
int cmp_rem_b() {
if (lr != lb) {
return lr > lb ? 1 : -1;
}
for (ll i = 0; i < lr; i++) {
if (rem[i] != b[i]) {
return rem[i] > b[i] ? 1 : -1;
}
}
return 0;
}
// 执行 rem = rem - b,要求调用前 rem >= b;b 的最低位对齐到 rem 的最低位。
void sub_b() {
ll shift = lr - lb; // b[j] 对齐到 rem 的下标 j + shift
ll borrow = 0;
for (ll i = lr - 1; i >= 0; i--) {
ll j = i - shift;
ll sub = (j >= 0) ? b[j] : 0;
ll t = rem[i] - sub - borrow;
if (t < 0) {
t += 10;
borrow = 1;
} else {
borrow = 0;
}
rem[i] = t;
}
// 去掉最高位上的前导零,保持 rem 是规范表示。
ll start = 0;
while (start + 1 < lr && rem[start] == 0) {
start++;
}
if (start > 0) {
for (ll i = start; i < lr; i++) {
rem[i - start] = rem[i];
}
lr -= start;
}
}
int main() {
if (scanf("%s", str_a) != 1) {
return 0;
}
if (scanf("%s", str_b) != 1) {
return 0;
}
la = strlen(str_a);
lb = strlen(str_b);
for (ll i = 0; i < la; i++) {
a[i] = str_a[i] - '0';
}
for (ll i = 0; i < lb; i++) {
b[i] = str_b[i] - '0';
}
lr = 1;
rem[0] = 0; // 余数从 0 开始
for (ll i = 0; i < la; i++) {
// 竖式的一步:把 A 的下一位拉下来,rem = rem * 10 + a[i]
rem[lr] = a[i];
lr++;
// 去掉新产生的前导零
ll start = 0;
while (start + 1 < lr && rem[start] == 0) {
start++;
}
if (start > 0) {
for (ll j = start; j < lr; j++) {
rem[j - start] = rem[j];
}
lr -= start;
}
// 反复减 B,减的次数就是这一位的商数字
ll digit = 0;
while (cmp_rem_b() >= 0) {
sub_b();
digit++;
}
q[lq] = digit;
lq++;
}
// 输出商,去掉前导零
ll qs = 0;
while (qs + 1 < lq && q[qs] == 0) {
qs++;
}
for (ll i = qs; i < lq; i++) {
printf("%d", q[i]);
}
printf("\n");
// 输出余数(rem 已无前导零,至少有一位)
for (ll i = 0; i < lr; i++) {
printf("%d", rem[i]);
}
printf("\n");
return 0;
}