Tuyển sinh 🎓 Mình nhận đào tạo lập trình giải thuật thi đấu: thi vào lớp 10 chuyên tin · thi học sinh giỏi tin học cấp tỉnh/thành · thi vào đội tuyển quốc gia · Olympic tin học sinh viên / ICPC · phỏng vấn thuật toán. Bấm vào đây để xem thông tin khóa học → Xem khóa học

Tìm hai số từ tổng và hiệu (Tin học trẻ toàn quốc 2025 – Vòng loại quốc gia đợt 3 – Bảng B, C2)

Tìm hai số từ tổng và hiệu
Nguồn: Tin học trẻ toàn quốc 2025 Độ khó: Dễ Toán Số nguyên lớn

Đề bài

Có hai số nguyên $A$ và $B$ (với $|A|, |B| \le 10^{100}$). Người ta đã tính sẵn tổng $A + B$ và hiệu $A - B$ của chúng. Rõ ràng khi biết tổng và hiệu thì ta tìm lại được hai số ban đầu.

Yêu cầu: cho tổng và hiệu của hai số nguyên, hãy tìm lại hai số đó.

Dữ liệu vào:

  • Dòng 1: số nguyên $A + B$.
  • Dòng 2: số nguyên $A - B$.

Kết quả:

  • Dòng 1: số $A$.
  • Dòng 2: số $B$.

Subtask:

  • Subtask 1 (90% số điểm): $|A|, |B| \le 10^9$.
  • Subtask 2 (10% số điểm): không có ràng buộc gì thêm.

Giới hạn thời gian: 1 giây. Dữ liệu nhập từ bàn phím và in ra màn hình.

Ví dụ – input
0
-200
Ví dụ – output
-100
100

Giải thích: $A = -100$, $B = 100$ cho $A + B = 0$ và $A - B = -200$.

Nguồn: Hội thi Tin học trẻ toàn quốc 2025 – Vòng loại quốc gia đợt 3 – Bảng B và Bảng C2 (bài AB). Đề được mình viết lại cho dễ đọc, giữ nguyên ý và số liệu.

Lời mở đầu

Đây là bài "cho điểm" của đợt 3 nhưng có một cái bẫy rất đặc trưng của các kỳ thi: 90% điểm chỉ cần một công thức lớp 8, còn 10% điểm cuối lại đòi số có tới 101 chữ số – vượt xa mọi kiểu số nguyên của C++. Mình sẽ đi từ công thức (ăn subtask 1), rồi tự cài đặt phép cộng, trừ, chia đôi trên xâu chữ số để lấy trọn điểm. Với Python thì... các bạn sẽ thấy nó "ăn gian" thế nào 😄.

Dành cho học viên · Ưu tiênNộp bài trên hệ thống CLUE OJ

Link này chỉ học viên đã tham gia tổ chức mới vào được. Học viên nên ưu tiên nộp ở đây vì sẽ được chấm nhanh hơn.

Nộp bài trên CLUE OJ

Lộ trình lời giải, bấm vào từng phần để nhảy tới:

Bộ testBộ test có gì?

Bộ test của mình gồm 20 test (sinh theo đúng tỉ lệ subtask của đề): test 1–18 thuộc subtask 1 ($|A|, |B| \le 10^9$), test 19–20 thuộc subtask 2 (số tới $10^{100}$). Test 1 là ví dụ của đề. Lời giải cuối cùng (cách 2) đã được nộp thử trên hệ thống chấm chính thức tinhoctre.vn và đạt 100/100 điểm.

NhómTestSố chữ số lớn nhất
của $A+B$, $A-B$
Số chữ số lớn nhất
của $A$, $B$
Số test có
$A$ hoặc $B$ âm
Số test có
$A$ hoặc $B$ bằng 0
Subtask 11–18101010/183/18
Subtask 219–201011012/20/2
  • Subtask 1 có các test biên: $A = B = 10^9$ (tổng bằng $2 \cdot 10^9$), $A = -B = 10^9$ (hiệu bằng $2 \cdot 10^9$), $A = B = 0$, $A$ hoặc $B$ âm... Test 3–6 có tổng hoặc hiệu bằng đúng $2 \cdot 10^9$.
  • Subtask 2 có test $A = 10^{100}$, $B = -10^{100}$ (hiệu có 101 chữ số) và một test ngẫu nhiên khoảng 100 chữ số.

Kết quả chạy thật của các cách bên dưới theo từng subtask:

SubtaskĐiều kiệnTestCách 1
long long
Cách 1
Python
Cách 2
số lớn tự cài
Subtask 1 (90%)$|A|, |B| \le 10^9$1–1818/1818/1818/18
Subtask 2 (10%)không ràng buộc thêm ($|A|, |B| \le 10^{100}$)19–200/22/22/2

Ý tưởngCộng và trừ hai phương trình

Gọi $S = A + B$ và $D = A - B$. Cộng hai đẳng thức lại, $B$ triệt tiêu; trừ hai đẳng thức, $A$ triệt tiêu:

$$S + D = 2A \;\Rightarrow\; A = \frac{S + D}{2}, \qquad S - D = 2B \;\Rightarrow\; B = \frac{S - D}{2}.$$

Vì $S + D = 2A$ là số chẵn nên phép chia 2 luôn chia hết, không phải lo làm tròn – kể cả khi số âm.

Phần khó chỉ là độ lớn: ở subtask 2, $S$ và $D$ có thể có 101 chữ số. Khi đó ta làm y như học sinh tiểu học: đặt tính theo cột để cộng/trừ từng chữ số (có nhớ, có mượn), rồi chia đôi từ hàng cao xuống. Em nhập $S$, $D$ rồi bấm Tiếp để xem máy "đặt tính" từng bước:

Ví dụ nhanh:

Cách 1Dùng công thức với long long (subtask 1)

Với $|A|, |B| \le 10^9$ thì $|S|, |D| \le 2 \cdot 10^9$. Số này vừa sát giới hạn của int ($2^{31} - 1 \approx 2{,}147 \cdot 10^9$): chỉ cần đề nới thêm một chút là tràn. Thói quen tốt là dùng luôn long long (tới khoảng $9{,}2 \cdot 10^{18}$) cho yên tâm.

C++▶ Chạy tay từng dòng codeCách 1: công thức A = (s + d) / 2, B = (s − d) / 2 với long long.
Dữ liệu mẫu:
  1. Phép chia / trong C++ làm tròn về 0, nhưng ở đây s + d và s - d luôn chẵn nên không có chuyện làm tròn, kết quả đúng cả với số âm (ví dụ $(0 + (-200)) / 2 = -100$).
  2. Bản Python giống hệt về công thức, nhưng kiểu int của Python không giới hạn số chữ số, nên nó tự đúng luôn cho subtask 2. Lưu ý dùng // (chia nguyên); dùng / sẽ ra số thực và mất chính xác khi số lớn.
Không gian$O(1)$
Thời gian$O(1)$

Kết quả chạy thật: bản C++ đúng 18/20 test, sai test 19–20, chạy lâu nhất 0.003s – test 19, 20 có số hơn 100 chữ số nên cin >> s không đọc nổi, mất 10% điểm của subtask 2. Bản Python đúng 20/20 test, chạy lâu nhất 0.023s – full điểm.

Cách 2Tự cài đặt số nguyên lớn (full điểm)

Ta lưu mỗi số dưới dạng dấu (neg) và trị tuyệt đối là một xâu chữ số (m). Cần 3 thao tác:

  • Cộng hai số có dấu add(x, y): nếu cùng dấu thì cộng hai trị tuyệt đối, giữ dấu chung; nếu khác dấu thì lấy trị tuyệt đối lớn trừ bé và mang dấu của số có trị tuyệt đối lớn hơn (ví dụ $3 + (-8) = -(8 - 3) = -5$).
  • Đổi dấu: $S - D = S + (-D)$, nên chỉ cần lật neg của $D$ rồi dùng lại hàm cộng.
  • Chia đôi trị tuyệt đối: đi từ chữ số hàng cao, cur = rem·10 + chữ số, ghi cur / 2 vào thương, mang cur % 2 sang hàng sau. Dấu giữ nguyên.
C++▶ Chạy tay từng dòng codeCách 2: số nguyên lớn lưu bằng (dấu, xâu chữ số) – cộng/trừ theo cột, chia đôi từ hàng cao.
Dữ liệu mẫu:
Thanh trên cùng của phần Dữ liệu là ngăn xếp lời gọi hàm (hàm đang chạy tô tím). Để dễ theo dõi, khung này nhận số tối đa 12 chữ số.
  1. cmpAbs: so sánh hai xâu chữ số (không có số 0 ở đầu) – xâu dài hơn thì lớn hơn; cùng độ dài thì so sánh như từ điển (a < b của string) là đúng thứ tự số.
  2. addAbs/subAbs duyệt hai con trỏ i, j từ cuối xâu (hàng đơn vị) về đầu, ghép kết quả vào r theo thứ tự ngược rồi reverse lại. Trong subAbs, sau khi trừ có thể còn số 0 ở đầu (ví dụ $100 - 99 = 001$) nên phải bỏ chúng đi.
  3. halfAbs cũng có thể sinh số 0 ở đầu ($14 / 2$ cho ra "07") nên phải cắt bỏ, nhưng giữ lại ít nhất 1 chữ số.
  4. Khi in, nếu kết quả là 0 thì không in dấu trừ (tránh in -0).
Không gian$O(L)$
Thời gian$O(L)$ với $L$ là số chữ số (≤ 101)

Kết quả chạy thật: bản C++ đúng 20/20 test, chạy lâu nhất 0.004s – full điểm. Mình cũng thử thêm 1500 bộ ngẫu nhiên (số từ 1 tới 100 chữ số, có các trường hợp $A = B$, $A = -B$, $B = 0$) so với đáp án tính bằng Python: khớp tất cả.

Tổng kếtKết quả chạy thật trên bộ test

CáchNgôn ngữĐúngLâu nhấtGhi chú
1. Công thức, long longC++18/200.003sSai 2 test số lớn → 90 điểm
1. Công thứcPython20/200.023sPython có sẵn số nguyên lớn → full điểm
2. Số lớn tự càiC++20/200.004sFull điểm
Ghi chú của mình: thời gian là thời gian CPU đo trên máy mình (giới hạn 1 giây) – mọi cách đều chạy tức thì. Bài học của bài này: đọc kỹ giới hạn từng subtask. Khi thấy $10^{100}$ thì phải nghĩ ngay tới xử lý số dưới dạng xâu (hoặc dùng Python). Ba thao tác cộng, trừ, chia cho số nhỏ trên số lớn là "đồ nghề" nên thuộc lòng, rất hay gặp lại. Lời giải cách 2 đã đạt 100/100 trên trang chấm chính thức tinhoctre.vn.
Rất vui khi anh em nào đã đọc đến những dòng cuối cùng này. Anh em đọc chắc cũng mỏi mắt luôn chứ 😅 vậy anh em nghĩ người viết ra hết đống ở trên còn thế nào nữa 🤪. Vậy nên đừng tiếc gì cho mình một lời bình luận hoặc chia sẻ bài viết để động viên mình nè. Chúc anh em học tốt và gặt hái nhiều thành công 😇

Nhận xét