Chuyên đề 7: Đọc ghi file và nhập xuất nhanh trong C++ và Python: freopen, đọc tới hết file, getline, định dạng output và những lỗi làm bài đúng mà 0 điểm
Lời mở đầu
Năm nào thầy cũng nghe vài câu chuyện giống hệt nhau sau kỳ thi HSG: “Em làm được 3 bài mà chỉ được 5 điểm”, “Bài của em chạy đúng mà máy chấm cho 0”. Mở bài làm ra xem thì thấy: file tên bai1.cpp.txt, quên freopen nên in ra màn hình, đọc nhầm dòng vì getline, in thêm câu “Ket qua la:”, hoặc dùng endl in một triệu dòng nên quá thời gian. Không lỗi nào liên quan tới thuật toán, nhưng mỗi lỗi đều đủ để mất trắng cả bài.
Ở bài 9 em đã biết máy chấm làm việc thế nào, cách dùng freopen cơ bản và mẹo tăng tốc nhập xuất; ở bài 2 em đã quen cin, cout. Chuyên đề này không giảng lại những thứ đó mà đi thẳng vào thực chiến: quy định nộp bài trong phòng thi, mọi dạng input hay gặp trong đề, định dạng output sao cho máy chấm nào cũng chịu, đọc ghi thật nhanh khi dữ liệu lớn, và một bảng kiểm tra trước khi nộp. Phần kỹ năng sinh test, chấm trâu em sẽ gặp lại kỹ hơn ở bài 22: Kỹ năng luyện thi.
BAI1.INP/BAI1.OUT, đặt freopen đúng chỗ và mẹo một bài nộp chạy được cả trên máy chấm lẫn web luyện tập; năm dạng input: biết trước $n$, nhiều bộ test, đọc tới hết file, đọc theo dòng với getline (và cái bẫy dòng rỗng, ký tự \r), mỗi dòng không biết trước bao nhiêu số; định dạng output: dấu cách, xuống dòng, số thực, dấu phân cách; nhập xuất nhanh với sync_with_stdio, cin.tie, '\n', scanf/printf, Python đọc một lần in một lần, và cách đo thời gian; bảng kiểm tra trước khi nộp và cách tự tạo file test. Có khung chạy tay, 3 mô phỏng bấm từng bước và 8 bài tập kèm gợi ý.Lộ trình bài viết, bấm vào từng phần để nhảy tới:
Phần 1Quy định nộp bài trong kỳ thi HSG
Ở kỳ thi HSG cấp tỉnh, thành phố, bài của em thường được chấm tự động bằng phần mềm (Themis, CMS hoặc một hệ thống tương tự). Máy chấm không đọc code, không hiểu ý em, nó chỉ làm đúng ba việc với từng test: đặt file dữ liệu vào cạnh chương trình, chạy chương trình trong giới hạn thời gian, rồi so file kết quả với đáp án. Đề thi luôn có một bảng như sau, em phải đọc thật kỹ trước khi viết dòng code đầu tiên:
| Bài | Tên file chương trình | File dữ liệu vào | File kết quả | Thời gian |
|---|---|---|---|---|
| Bài 1 | BAI1.CPP hoặc BAI1.PY | BAI1.INP | BAI1.OUT | 1 giây |
| Bài 2 | DAYSO.CPP hoặc DAYSO.PY | DAYSO.INP | DAYSO.OUT | 1 giây |
| Bài 3 | LUOI.CPP hoặc LUOI.PY | LUOI.INP | LUOI.OUT | 2 giây |
Bảng 1. Bảng quy định tên file điển hình của một đề HSG. Tên chương trình, tên file vào và file ra phải giống hệt từng chữ cái.
Hình 1. Bên trái: thư mục bài làm của thí sinh (thường đặt tên theo số báo danh), chỉ chứa file nguồn đúng tên. Bên phải: với mỗi test, máy chấm chạy chương trình, đọc BAI1.OUT em ghi ra rồi so với đáp án.
Năm quy tắc vàng khi nộp bài:
- Tên file nguồn đúng như đề:
BAI1.CPP, không phảiBai1.cpp,bai 1.cpphayBAI1.cpp.cpp. Máy chấm chạy trên Linux phân biệt chữ hoa, chữ thường; Windows thì không, nên em chạy trên máy mình thấy đúng mà lên máy chấm lại không tìm thấy file. Cách an toàn duy nhất: chép nguyên văn tên trong đề. freopenđúng tên, đúng chế độ, đúng chỗ:"r"cho file vào,"w"cho file ra, đặt ngay đầu hàmmain, trước mọi lệnh đọc ghi. Chỉ ghi tên file, không ghi đường dẫn như"D:\\BAITHI\\BAI1.INP", vì máy chấm đặt file ở thư mục của nó.- Không in lời nhắc: không có
cout << "Nhap n: ", không cóinput("Nhap n: "), không có “Ket qua la”. Mọi chữ in ra đều bị đem đi so với đáp án. - Không dừng màn hình: bỏ
system("pause"),getch(),#include <conio.h>. Máy chấm không có ai bấm phím, chương trình sẽ đứng chờ tới khi quá thời gian. Kết thúc bằngreturn 0;. - Mỗi bài một file, nộp đúng thư mục: không để file
.exe, không để thư mục con lồng nhau, không nộp nhầm bản nháp.
BAI1.INP bằng Notepad, tên thật có thể là BAI1.INP.txt. Chương trình mở BAI1.INP không thấy gì, đọc ra toàn số 0. Hãy bật hiện đuôi file (File Explorer → View → File name extensions) ngay khi vào phòng thi, và khi lưu bằng Notepad chọn “Save as type: All files”.2. Ghi nhầm chế độ. Gõ nhầm
freopen("BAI1.INP", "w", stdin) sẽ xoá sạch file dữ liệu ngay khi chạy. Em nên chép khung chuẩn bên dưới chứ đừng gõ tay mỗi lần.Một bài nộp chạy được ở mọi nơi. Khi luyện trên web chấm (Codeforces, VNOJ, CLUE OJ…), dữ liệu đi qua bàn phím và màn hình (stdin, stdout), không có file. Còn trong phòng thi lại bắt buộc dùng file. Thay vì nhớ thêm, bớt freopen mỗi lần (rất dễ quên!), em dùng mẹo: thử mở file trước, mở được thì mới chuyển hướng. Trên máy chấm có BAI1.INP nên chương trình đọc ghi file; trên web không có file nên vẫn dùng stdin, stdout bình thường. Đây là khung thầy khuyên em dùng cho mọi bài:
Xem code C++ / Python: khung bài nộp chuẩn: đọc ghi file khi có file, ngược lại dùng bàn phím (bấm để mở)
#include <bits/stdc++.h>
using namespace std;
int main(){
// Có file BAI1.INP thì đọc ghi file, không có thì dùng bàn phím và màn hình
if(fopen("BAI1.INP", "r")){
freopen("BAI1.INP", "r", stdin);
freopen("BAI1.OUT", "w", stdout);
}
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n; // KHÔNG in "Nhap n:" trước khi đọc
long long s = 0;
for(int i = 0; i < n; i++){
long long x;
cin >> x;
s += x;
}
cout << s << "\n"; // chỉ in đúng thứ đề yêu cầu
return 0; // KHÔNG system("pause"), KHÔNG getch()
}
import sys, os
# Có file BAI1.INP thì đọc ghi file, không có thì dùng bàn phím và màn hình
if os.path.exists('BAI1.INP'):
sys.stdin = open('BAI1.INP', 'r')
sys.stdout = open('BAI1.OUT', 'w')
data = sys.stdin.read().split() # KHÔNG dùng input("Nhap n: ")
n = int(data[0])
s = sum(map(int, data[1:n + 1]))
print(s) # chỉ in đúng thứ đề yêu cầu
sys.stdout.flush() # đảm bảo dữ liệu đã ghi hết ra file
int main(){
if(fopen("BAI1.INP", "r")){ // tên lấy NGUYÊN VĂN từ đề
freopen("BAI1.INP", "r", stdin); // "r": đọc
freopen("BAI1.OUT", "w", stdout); // "w": ghi
}
ios::sync_with_stdio(false);
cin.tie(nullptr);
// ... đọc, xử lý, in ...
return 0;
}Đổi đúng 3 chỗ BAI1 thành tên bài trong đề. Với Python, thay bằng os.path.exists và gán lại sys.stdin, sys.stdout như code ở trên. Khi đã chuyển sys.stdout sang file, dùng print như bình thường; trước khi thoát nên gọi sys.stdout.flush() cho chắc.In thông tin gỡ lỗi thế nào cho an toàn? Trong lúc làm bài em hay in thêm giá trị biến để kiểm tra. Nếu in bằng cout/print thì nó lẫn vào kết quả, quên xoá là sai. Hãy in ra luồng lỗi: cerr << "x = " << x << "\n"; trong C++, print("x =", x, file=sys.stderr) trong Python. Luồng lỗi hiện lên màn hình khi em chạy thử nhưng không bị ghi vào file kết quả. Dù vậy, trước khi nộp vẫn nên xoá đi vì in nhiều cũng làm chậm chương trình.
Phần 2Các dạng input hay gặp và cách đọc từng dạng
Điều quan trọng nhất cần hiểu: với chương trình, dữ liệu vào chỉ là một dãy ký tự liên tiếp, có một con trỏ đọc chạy từ đầu tới cuối. Dấu xuống dòng cũng chỉ là một ký tự ('\n'). Mỗi cách đọc di chuyển con trỏ theo một luật riêng:
cin >> x(vàscanf): bỏ qua mọi khoảng trắng (dấu cách, tab, xuống dòng) đứng trước, đọc một “từ”, rồi dừng ngay sau ký tự cuối của từ đó. Ký tự xuống dòng phía sau vẫn nằm nguyên.getline(cin, s): đọc mọi thứ từ con trỏ tới hết dòng (kể cả dấu cách), nuốt luôn ký tự xuống dòng nhưng không đưa vàos.- Python
input(): đọc một dòng, bỏ'\n'ở cuối.sys.stdin.read().split(): đọc hết rồi tách theo mọi khoảng trắng, xuống dòng ở đâu cũng không quan trọng.
Em xem con trỏ chạy trong mô phỏng dưới đây. Chương trình mặc định là cái bẫy kinh điển: đọc n bằng cin rồi đọc tên bằng getline. Sau đó em thử chương trình thứ ba (cách sửa) và chế độ xuống dòng kiểu Windows:
Giờ ta đi qua năm dạng input mà đề thi hay dùng. Bảng sau là “bản đồ” để em nhận dạng nhanh:
| Dạng | Đề viết kiểu | C++ | Python |
|---|---|---|---|
| 1. Biết trước số lượng | “Dòng đầu là $n$, dòng sau gồm $n$ số” | cin >> n; rồi for $n$ lần | data = sys.stdin.read().split() |
| 2. Nhiều bộ test | “Dòng đầu là $T$, tiếp theo là $T$ bộ dữ liệu” | for(t = 1..T), đặt lại biến mỗi bộ | đọc hết, dùng chỉ số p chạy dần |
| 3. Tới hết file | “Gồm nhiều dòng, mỗi dòng…” (không cho số lượng) | while(cin >> x) | for x in data |
| 4. Theo dòng, có dấu cách | “Mỗi dòng là họ tên / một câu văn” | getline, nhớ bỏ phần còn lại của dòng trước | .split('\n'), rstrip('\r') |
| 5. Mỗi dòng không biết trước bao nhiêu số | “Dòng thứ $i$ gồm các số của nhóm $i$” | getline + stringstream | line.split() |
Bảng 2. Năm dạng input. Dạng 1 em đã quen từ bài 2; phần này tập trung vào bốn dạng còn lại.
Dạng 2: nhiều bộ test trong một file
Đề cho $T$ bộ dữ liệu liên tiếp, yêu cầu in kết quả mỗi bộ trên một dòng, thường kèm định dạng như Case #1: …. Hai lỗi kinh điển: quên đặt lại biến (tổng, mảng đếm, cờ đánh dấu) khi sang bộ mới, và dừng đọc giữa chừng (ví dụ tìm thấy đáp án rồi break khỏi vòng đọc), làm số còn lại của bộ này bị đọc nhầm sang bộ sau. Quy tắc: luôn đọc hết dữ liệu của bộ test trước, xử lý sau; và khai báo biến bên trong vòng lặp test để nó tự được tạo mới. Em chạy tay chương trình dưới đây, để ý dòng s = 0:
memset mỗi bộ là $10^{10}$ phép, quá thời gian. Hãy chỉ xoá đúng những ô đã dùng (duyệt lại các số của bộ test và đặt cnt[a[i]] = 0), hoặc dùng vector có kích thước $n$ của chính bộ đó. Đề thường cho thêm điều kiện “tổng $n$ của mọi bộ không quá $2 \cdot 10^5$” chính là để em làm như vậy.Dạng 3: đọc tới hết file
Khi đề không cho số lượng (“file gồm nhiều số nguyên”, “mỗi dòng một cặp số, không biết có bao nhiêu dòng”), ta đọc cho tới khi hết dữ liệu. Trong C++, biểu thức cin >> x trả về “đúng” nếu đọc được, “sai” nếu hết dữ liệu hoặc gặp ký tự không phải số, nên đặt thẳng vào while. Cách đọc từng cặp: while(cin >> a >> b). Python thì đơn giản nhất là đọc hết rồi tách. (Khung chạy tay “đọc số tới khi hết dữ liệu” em đã gặp ở bài 9.)
Xem code C++ / Python: đọc số tới hết file, in số lượng, tổng và số lớn nhất (bấm để mở)
#include <bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(false); cin.tie(nullptr);
long long x, s = 0, mx = LLONG_MIN;
int cnt = 0;
while(cin >> x){ // đọc được thì làm, hết dữ liệu thì dừng
cnt++;
s += x;
mx = max(mx, x);
}
cout << cnt << " " << s << " " << mx << "\n";
return 0;
}
import sys
a = list(map(int, sys.stdin.buffer.read().split())) # đọc hết, tách theo mọi khoảng trắng
print(len(a), sum(a), max(a))
while(!cin.eof()){ cin >> x; ... }. Nếu file kết thúc bằng một dấu xuống dòng (gần như luôn như vậy), sau khi đọc số cuối, eof() vẫn chưa đúng vì còn ký tự '\n'; vòng lặp chạy thêm một lần, cin >> x thất bại nhưng em vẫn xử lý x → số cuối bị tính hai lần. Luôn đặt chính lệnh đọc làm điều kiện của while. Tương tự trong Python: đừng gọi input() trong while True mà không bắt lỗi hết dữ liệu; hãy đọc hết bằng sys.stdin.read().Một biến thể rất hay gặp trong đề cũ: dữ liệu kết thúc bằng một dòng đặc biệt như 0 0 hay -1 (gọi là lính canh). Khi đó em đọc từng cặp, gặp lính canh thì break ngay, không xử lý nó và không đọc gì thêm, vì phía sau có thể còn dữ liệu rác (bài tập 2).
Dạng 4: đọc theo dòng, dòng có dấu cách
Họ tên, câu văn, địa chỉ… chứa dấu cách nên cin >> s chỉ đọc được một từ. Phải dùng getline. Nhưng như mô phỏng ở trên cho thấy, sau cin >> n con trỏ dừng trước ký tự xuống dòng, nên getline đầu tiên đọc ra xâu rỗng. Hai cách sửa:
cin.ignore(numeric_limits<streamsize>::max(), '\n');bỏ mọi thứ tới hết dòng (kể cả dấu cách thừa sau $n$). Dùngcin.ignore()không tham số chỉ bỏ một ký tự, sẽ hỏng nếu sau $n$ còn dấu cách.cin >> n >> ws;bỏ mọi khoảng trắng tới ký tự thật đầu tiên. Gọn, nhưng nếu dòng tiếp theo cố tình là dòng trống hoặc bắt đầu bằng dấu cách có ý nghĩa thìwsnuốt mất.
Ký tự '\r' từ Windows. Trên Windows, xuống dòng là hai ký tự \r\n; trên Linux chỉ là \n. Nếu file test được soạn trên Windows mà máy chấm chạy Linux, getline trả về xâu còn dính '\r' ở cuối: in ra nhìn thì giống, nhưng s.size() lệch 1, so sánh s == "YES" thành sai. Python cũng vậy: input() trên Linux trả về 'abc\r'. Thói quen tốt: sau mỗi lần đọc dòng, xoá '\r' ở cuối nếu có. cin >> và split() thì không bị ảnh hưởng vì coi '\r' là khoảng trắng.
Xem code C++ / Python: đọc n rồi n dòng họ tên có dấu cách (an toàn với \r) (bấm để mở)
#include <bits/stdc++.h>
using namespace std;
int main(){
int n;
cin >> n;
cin.ignore(numeric_limits<streamsize>::max(), '\n'); // bỏ phần còn lại của dòng đầu (kể cả '\n')
for(int i = 1; i <= n; i++){
string s;
getline(cin, s);
if(!s.empty() && s.back() == '\r') s.pop_back(); // file soạn trên Windows: xoá '\r'
cout << i << ". " << s << " (" << s.size() << ")\n";
}
return 0;
}
import sys
lines = sys.stdin.buffer.read().decode().split('\n')
n = int(lines[0])
for i in range(1, n + 1):
s = lines[i].rstrip('\r') # file soạn trên Windows: xoá '\r'
print(f"{i}. {s} ({len(s)})")
Dạng 5: mỗi dòng không biết trước bao nhiêu số
Ví dụ: “Mỗi dòng ghi điểm các bài kiểm tra của một học sinh, số bài của mỗi bạn khác nhau”. Nếu đọc bằng cin >> x liên tục, em không biết số nào thuộc dòng nào vì cin coi xuống dòng như dấu cách. Cách làm: đọc cả dòng bằng getline, rồi biến xâu đó thành một luồng nhỏ stringstream và đọc số từ luồng nhỏ cho tới khi hết dòng. Python chỉ cần line.split(). Đừng quên dòng trống (học sinh không có bài nào) vẫn là một dòng hợp lệ.
Xem code C++ / Python: mỗi dòng một nhóm số: in số lượng và tổng của từng dòng (bấm để mở)
#include <bits/stdc++.h>
using namespace std;
int main(){
string line;
while(getline(cin, line)){ // mỗi dòng là một nhóm, không biết trước có mấy số
stringstream ss(line); // biến dòng thành một "luồng nhỏ"
long long x, s = 0;
int cnt = 0;
while(ss >> x){ cnt++; s += x; } // đọc tới hết dòng
cout << cnt << " " << s << "\n";
}
return 0;
}
import sys
out = []
for line in sys.stdin.buffer.read().decode().splitlines(): # mỗi dòng là một nhóm
a = list(map(int, line.split())) # split() tự bỏ '\r', dấu cách thừa
out.append(f"{len(a)} {sum(a)}")
print('\n'.join(out))
Phần 3Định dạng output: máy chấm so từng ký tự
Sau khi chạy xong, máy chấm so file kết quả của em với đáp án. Mỗi hệ thống có một kiểu so: có nơi so tuyệt đối từng ký tự, có nơi bỏ qua dấu cách thừa cuối dòng và dòng trống cuối file, có nơi (như phần lớn bài trên Codeforces) chỉ so từng số, từng từ. Em không được trông chờ vào kiểu dễ tính: hãy viết output sao cho qua được cả kiểu khắt khe nhất. Mô phỏng dưới đây cho em thấy cùng một output có thể AC ở máy chấm này mà WA ở máy chấm khác:
Một số chỗ em nên thử trong mô phỏng: xoá dấu cách cuối dòng 2 của “Output của em”; gõ Ket qua: 6 vào dòng 1 (thừa lời nhắc); viết 61 2 3 trên một dòng (quên xuống dòng); thêm một dòng trống ở cuối. Những quy tắc rút ra:
| Đề yêu cầu | Viết đúng (C++) | Viết đúng (Python) | Lỗi hay gặp |
|---|---|---|---|
| Các số trên một dòng, cách nhau một dấu cách | cout << a[i] << (i + 1 < n ? " " : "\n"); | print(*a) | quên xuống dòng sau dòng cuối, in dính số: 123 |
| Mỗi số một dòng | cout << x << '\n'; | print('\n'.join(map(str, a))) | dùng endl khi in nhiều (chậm, xem phần 4) |
Cách nhau dấu phẩy: 1, 2, 3 | in ", " trước mọi số trừ số đầu | print(*a, sep=', ') | thừa dấu phẩy cuối: 1, 2, 3, |
| Số thực, đúng 2 chữ số sau dấu chấm | cout << fixed << setprecision(2) << x; | print(f"{x:.2f}") | quên fixed nên in 1.2e+02 hoặc 3.1 |
| YES / NO | in đúng chữ hoa như đề | như C++ | in Yes, yes, CO |
Định dạng có chữ: Case #1: 5 | chép nguyên văn từ đề | f-string: f"Case #{t}: {s}" | Case 1: 5, case #1:5 |
| Ma trận $n \times m$ | mỗi hàng một dòng | print(' '.join(map(str, row))) | in cả ma trận trên một dòng; in theo cột |
Bảng 3. Các định dạng output thường gặp. Cột cuối là những lỗi thầy gặp nhiều nhất khi chấm bài học sinh.
Kỹ thuật “in dấu phân cách trước”. Muốn các số cách nhau bởi một dấu hiệu (dấu cách, dấu phẩy, dấu +…) mà không thừa ở cuối, cách chắc chắn nhất là: với mọi phần tử trừ phần tử đầu, in dấu phân cách rồi mới in phần tử. Không cần biết phần tử nào là cuối, nên dùng được cả khi đang duyệt và lọc (không biết trước sẽ in bao nhiêu số). Chạy tay để thấy dấu phẩy luôn nằm giữa hai số:
Code dưới đây gom ba kiểu in hay gặp nhất: dãy cách nhau một dấu cách, dãy có ngoặc và dấu phẩy, số thực đúng 2 chữ số sau dấu chấm. Chuyện làm tròn, sai số số thực em đã học kỹ ở chuyên đề 2; ở đây chỉ cần nhớ: muốn đúng k chữ số sau dấu chấm thì C++ phải có cả fixed và setprecision(k), Python dùng f"{x:.kf}".
Xem code C++ / Python: in dãy theo ba định dạng và số thực 2 chữ số thập phân (bấm để mở)
#include <bits/stdc++.h>
using namespace std;
int main(){
int n;
cin >> n;
vector<long long> a(n);
long long s = 0;
for(auto &x : a){ cin >> x; s += x; }
// Dòng 1: cách nhau một dấu cách, không thừa dấu cách cuối dòng
for(int i = 0; i < n; i++) cout << a[i] << (i + 1 < n ? " " : "\n");
// Dòng 2: dạng [a1, a2, ..., an]
cout << "[";
for(int i = 0; i < n; i++){
if(i > 0) cout << ", ";
cout << a[i];
}
cout << "]\n";
// Dòng 3: trung bình cộng, đúng 2 chữ số sau dấu chấm
cout << fixed << setprecision(2) << (double)s / n << "\n";
return 0;
}
import sys
data = sys.stdin.read().split()
n = int(data[0])
a = list(map(int, data[1:n + 1]))
print(*a) # cách nhau một dấu cách (mặc định sep=' ')
print('[' + ', '.join(map(str, a)) + ']') # hoặc print('[', end=''); print(*a, sep=', ', end=']\n')
print(f"{sum(a) / n:.2f}") # đúng 2 chữ số sau dấu chấm
cout << a[i] << " " vẫn thường được chấp nhận. Nhưng nếu đề viết “không có dấu cách thừa” hoặc dùng trình so khớp tuyệt đối, em sẽ mất điểm. Viết sạch ngay từ đầu tốn thêm đúng một dấu ?:, rẻ hơn rất nhiều so với rủi ro. Ngược lại, luôn kết thúc dòng cuối bằng \n: thiếu nó gần như không bao giờ bị trừ điểm, còn thừa dòng trống ở giữa thì luôn sai.Phần 4Nhập xuất nhanh: khi đọc ghi cũng có thể quá thời gian
Với $n = 10^6$ số, chỉ riêng việc đọc vào bằng cin mặc định đã mất cỡ 0,3 giây, in ra một triệu dòng bằng endl có thể mất cả giây: thuật toán $O(n)$ mà vẫn TLE! Lý do nằm ở ba chỗ, mỗi chỗ sửa bằng một dòng code:
ios::sync_with_stdio(false);Mặc định,cin/coutphải “đồng bộ” vớiscanf/printfcủa C để em trộn chung cũng không lộn xộn, cái giá là chậm. Tắt đồng bộ thìcinnhanh ngangscanf. Đổi lại, từ đó không được trộncin/coutvớiscanf/printf/putstrong cùng chương trình.cin.tie(nullptr);Mặc địnhcinbị “buộc” vớicout: trước mỗi lần đọc,coutbị xả bộ đệm (để người dùng kịp thấy lời nhắc “Nhap n:”). Khi chấm bài không có ai đọc lời nhắc, cởi dây buộc ra.'\n'thay choendl.endl= xuống dòng và xả bộ đệm. Mỗi lần xả là một lần nhờ hệ điều hành ghi ra đĩa, chậm hơn hàng trăm lần so với chép chữ vào bộ nhớ.
Bộ đệm là gì và “xả” là gì thì nhìn là hiểu ngay. Em chọn lần lượt ba cách in rồi đếm số lần xả, bước cuối có ước lượng thời gian cho một triệu dòng:
Xem code C++ / Python: nhập xuất nhanh với cin/cout và Python đọc một lần, in một lần (bấm để mở)
#include <bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(false); // tách cin/cout khỏi scanf/printf: nhanh hơn nhiều
cin.tie(nullptr); // cin không bắt cout xả bộ đệm trước mỗi lần đọc
int n;
cin >> n;
long long s = 0;
for(int i = 0; i < n; i++){
long long x;
cin >> x;
s += x;
cout << s << '\n'; // '\n' chứ KHÔNG dùng endl
}
return 0;
}
import sys
def main():
data = sys.stdin.buffer.read().split() # đọc MỘT lần toàn bộ dữ liệu
n = int(data[0])
out = []
s = 0
for i in range(1, n + 1):
s += int(data[i])
out.append(s)
sys.stdout.write('\n'.join(map(str, out)) + '\n') # in MỘT lần
main()
scanf/printf là lựa chọn nhanh khác cho C++, đặc biệt khi cần in số thực với định dạng phức tạp (printf("%.3f", x)) hoặc đọc dữ liệu có ký tự xen giữa như giờ 12:30 (scanf("%d:%d", &h, &m)). Nhớ đúng mã định dạng: %d cho int, %lld cho long long, %lf khi đọc double, %s cho mảng char. Sai mã định dạng không báo lỗi biên dịch mà cho kết quả rác.
Xem code C++ / Python: cùng bài với scanf/printf và itertools.accumulate (bấm để mở)
#include <bits/stdc++.h>
using namespace std;
int main(){
int n;
scanf("%d", &n);
long long s = 0;
for(int i = 0; i < n; i++){
long long x;
scanf("%lld", &x); // long long đi với %lld
s += x;
printf("%lld\n", s);
}
return 0; // dùng scanf/printf thì KHÔNG trộn với cin/cout đã tắt sync
}
import sys
from itertools import accumulate
data = sys.stdin.buffer.read().split()
n = int(data[0])
print('\n'.join(map(str, accumulate(map(int, data[1:n + 1])))))
Python khi dữ liệu lớn. Mỗi lời gọi input() và print() đều có chi phí đáng kể. Với $10^5$ dòng trở lên, dùng ba thói quen sau (chuyên đề 8 sẽ bàn kỹ hơn về Python khi đi thi):
- Đọc một lần:
data = sys.stdin.buffer.read().split()rồi lấy dần bằng chỉ số. Nhanh gấp nhiều lần gọiinput()từng dòng. - In một lần: gom kết quả vào list, cuối cùng
sys.stdout.write('\n'.join(map(str, res)) + '\n'). - Nếu thật sự phải đọc theo dòng:
input = sys.stdin.readline(nhớ dòng đọc được còn dính'\n', dùng.split()hoặc.rstrip()).
| Cách làm (đọc $10^6$ số, in $10^6$ dòng) | C++ | Python |
|---|---|---|
Mặc định, endl / input() + print() từng dòng | khoảng 1 – 2 giây | khoảng 2 – 4 giây |
Tắt sync + cin.tie(0) nhưng vẫn endl | khoảng 0,5 – 1 giây | – |
Tắt sync + cin.tie(0) + '\n' / scanf, printf | khoảng 0,1 giây | – |
| Python đọc một lần, in một lần | – | khoảng 0,5 giây |
Bảng 4. Thời gian tham khảo trên một máy chấm thông thường. Số cụ thể tuỳ máy, nhưng thứ tự nhanh chậm thì không đổi.
auto t0 = chrono::steady_clock::now();
// ... toàn bộ đọc, xử lý, in ...
double sec = chrono::duration<double>(chrono::steady_clock::now() - t0).count();
cerr << "Thoi gian: " << sec << " s\n";import time, sys
t0 = time.perf_counter()
# ... toàn bộ đọc, xử lý, in ...
print("Thoi gian:", time.perf_counter() - t0, "s", file=sys.stderr)Nếu chạy trên máy em đã gần giới hạn thời gian của đề thì trên máy chấm cũng nguy hiểm. Nhớ xoá đoạn đo trước khi nộp. Cách ước lượng số phép tính từ giới hạn đề em xem lại ở bài 9 và bài 22.Phần 5Bảng kiểm tra trước khi nộp và cách tự tạo file test
Mười lăm phút cuối giờ thi là lúc dễ mất điểm oan nhất: sửa vội một chỗ, quên bật lại freopen, nộp nhầm file. Thầy luôn bắt học sinh dò lại từng bài theo bảng dưới đây, mất khoảng một phút một bài:
| # | Kiểm tra | Cách kiểm tra nhanh |
|---|---|---|
| 1 | Tên file nguồn đúng từng chữ, đúng đuôi, đúng thư mục | bật hiện đuôi file, so với bảng trong đề |
| 2 | freopen / open đúng tên file vào, file ra; "r" và "w" không nhầm | đọc lại 3 dòng đầu main |
| 3 | Không còn lời nhắc, không còn dòng in gỡ lỗi ra cout/print | tìm chữ cout, print trong code |
| 4 | Không có system("pause"), getch(), conio.h | tìm chữ pause, conio |
| 5 | Kiểu dữ liệu đủ lớn (tổng, tích dùng long long) | nhân thử giá trị lớn nhất trong đề |
| 6 | Định dạng output đúng mẫu: chữ hoa thường, dấu cách, số chữ số thập phân | so ví dụ trong đề với output từng ký tự |
| 7 | Chạy đúng ví dụ trong đề bằng file (không gõ tay) | tạo BAI1.INP, chạy, mở BAI1.OUT |
| 8 | Chạy được test biên: $n$ nhỏ nhất, $n$ lớn nhất, số âm, toàn số bằng nhau | tự tạo file test (bên dưới) |
| 9 | Test lớn nhất chạy kịp thời gian | sinh test lớn, đo thời gian |
| 10 | Biên dịch lại bản cuối cùng không lỗi, đúng chuẩn C++ của kỳ thi | bấm biên dịch lần cuối sau lần sửa cuối |
Bảng 5. Mười điều cần dò trước khi nộp. In ra, dán cạnh máy khi luyện đề cho thành phản xạ.
Tự tạo file test bằng tay. Mở Notepad (hoặc chính Code::Blocks), gõ dữ liệu, lưu với tên BAI1.INP cùng thư mục với file nguồn. Đừng chỉ chép ví dụ trong đề: hãy viết thêm các test “khó chịu” mà đề cho phép: $n = 1$; mọi số bằng nhau; số âm lớn nhất; dòng trống ở cuối file; file không có dấu xuống dòng ở dòng cuối; nhiều dấu cách giữa các số. Mỗi test em tự tính đáp án bằng tay rồi so.
Sinh test lớn bằng chương trình. Test $n = 10^6$ thì không gõ tay được. Viết một chương trình nhỏ in ra dữ liệu ngẫu nhiên, rồi chuyển hướng output vào file: gen < thamso.txt > BAI1.INP (C++ đã biên dịch) hoặc python gen.py < thamso.txt > BAI1.INP. Dấu > trong cửa sổ lệnh có tác dụng y như freopen ra file. Code dưới đây tự viết bộ sinh số giả ngẫu nhiên đơn giản, nên C++ và Python cho đúng cùng một bộ test với cùng tham số, tiện cho việc so sánh hai bài với nhau:
Xem code C++ / Python: sinh test ngẫu nhiên từ tham số seed, n, lo, hi (bấm để mở)
#include <bits/stdc++.h>
using namespace std;
long long seed;
long long rnd(){ // số giả ngẫu nhiên tự viết, giống hệt bản Python
seed = (seed * 1103515245 + 12345) % 2147483648LL;
return seed;
}
int main(){
long long n, lo, hi;
cin >> seed >> n >> lo >> hi; // chạy: gen < thamso.txt > BAI1.INP
cout << n << "\n";
for(int i = 0; i < n; i++)
cout << lo + rnd() % (hi - lo + 1) << (i + 1 < n ? " " : "\n");
return 0;
}
seed, n, lo, hi = map(int, input().split()) # chạy: python gen.py < thamso.txt > BAI1.INP
def rnd(): # số giả ngẫu nhiên tự viết, giống hệt bản C++
global seed
seed = (seed * 1103515245 + 12345) % 2147483648
return seed
print(n)
print(' '.join(str(lo + rnd() % (hi - lo + 1)) for _ in range(n)))
Khi đã có bộ sinh test, bước tiếp theo là viết thêm một bài “trâu” (vét cạn, chắc chắn đúng nhưng chậm) và cho hai bài chạy trên hàng trăm test ngẫu nhiên rồi so kết quả: đó là kỹ thuật chấm trâu, thầy đã hướng dẫn chi tiết kèm script tự động ở bài 22. Với chuyên đề này, em chỉ cần thuộc lòng: khung bài nộp chuẩn, năm dạng input, dấu phân cách in trước và bộ ba nhập xuất nhanh.
freopen cho từng bài, biên dịch thử. Làm xong bước này khi đầu óc còn tỉnh táo, em sẽ không bao giờ phải lo chuyện tên file vào phút cuối. Khi luyện đề ở nhà hay thi thử trên CLUE OJ, hãy tập luôn thói quen này.- Cách hạn chế lỗi Wrong Answer khi nộp bài trên các web chấm code (Kinh nghiệm)
- Hướng dẫn tham gia Thi thử HSG Tin học hàng tuần trên ClueOJ (Hướng dẫn)
- Xuất các từ trong chuỗi, mỗi từ một dòng (Dễ)
- Tính Tổng Các Số Trong Xâu (Dễ)
- Cắt Hình (HSG9 Hà Nội 2025 – Bài 1) (Dễ)
- Đếm Ký Tự (HSG9 TP Đà Nẵng 2018-2019 – Bài 1) (Dễ)
- Khóa Số (HSG12 Hà Nội 2025 – Bài 1) (Dễ)
Bài tậpLuyện đọc ghi đúng định dạng
Tám bài dưới đây thuật toán đều nhẹ nhàng, cái khó nằm ở đọc đúng và in đúng. Em hãy làm như trong phòng thi: dùng khung bài nộp chuẩn, tạo file .INP từ ví dụ, tự thêm test biên, và kiểm tra output từng ký tự.
File dữ liệu gồm các số nguyên $x$ ($|x| \le 10^9$) viết trên một hoặc nhiều dòng, giữa các số có thể có nhiều dấu cách, có thể có dòng trống. Số lượng số không cho trước (ít nhất 1, nhiều nhất $10^6$). In ra số lượng số trên dòng thứ nhất và tổng của chúng trên dòng thứ hai.
3 5
-2
10 45
20📝 Nộp bài, chấm tự động trên ClueOJ ↗
💡 Gợi ý
while(cin >> x) tự bỏ qua mọi dấu cách, dòng trống. Tổng lớn nhất cỡ $10^{15}$ nên dùng long long. Python: sys.stdin.buffer.read().split(). Mẫu có 5 số 3, 5, −2, 10, 4 với tổng 20.Xem code tham khảo (C++ / Python)
#include <bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(false); cin.tie(nullptr);
long long x, s = 0;
int cnt = 0;
while(cin >> x){
cnt++;
s += x;
}
cout << cnt << "\n" << s << "\n";
return 0;
}
import sys
a = sys.stdin.buffer.read().split()
print(len(a))
print(sum(map(int, a)))
Mỗi dòng của file dữ liệu ghi hai số nguyên dương $a$, $b$ ($a, b \le 10^{18}$). Dữ liệu kết thúc bởi dòng 0 0 (không xử lý dòng này); sau dòng 0 0 có thể còn dữ liệu khác, em phải bỏ qua. Với mỗi cặp, in ước chung lớn nhất của $a$ và $b$ trên một dòng. Có không quá $10^5$ cặp.
12 18
7 5
100 75
0 0
9 36
1
25📝 Nộp bài, chấm tự động trên ClueOJ ↗
💡 Gợi ý
while, gặp 0 0 thì break ngay. Dòng 9 3 nằm sau lính canh nên không được in. Số tới $10^{18}$ nên dùng long long; C++17 có sẵn gcd trong <numeric>, Python có math.gcd.Xem code tham khảo (C++ / Python)
#include <bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(false); cin.tie(nullptr);
long long a, b;
while(cin >> a >> b){
if(a == 0 && b == 0) break; // lính canh: dừng, không xử lý
cout << gcd(a, b) << "\n";
}
return 0;
}
import sys
from math import gcd
data = sys.stdin.buffer.read().split()
out = []
for i in range(0, len(data) - 1, 2):
a, b = int(data[i]), int(data[i + 1])
if a == 0 and b == 0: # lính canh: dừng, không xử lý
break
out.append(gcd(a, b))
print('\n'.join(map(str, out)))
Có $T$ bộ test ($T \le 10^4$). Mỗi bộ gồm dòng đầu là $n$ ($1 \le n$, tổng $n$ của mọi bộ không quá $2 \cdot 10^5$), dòng sau là $n$ số nguyên $|a_i| \le 10^9$. Với bộ thứ $t$, in một dòng đúng dạng Case #t: d, trong đó $d$ là hiệu giữa số lớn nhất và số nhỏ nhất của bộ đó.
2
3
4 9 1
1
-5Case #1: 8
Case #2: 0📝 Nộp bài, chấm tự động trên ClueOJ ↗
💡 Gợi ý
mx, mn bên trong vòng lặp test để mỗi bộ bắt đầu lại từ đầu. Hiệu có thể tới $2 \cdot 10^9$, vượt int. In đúng từng ký tự: Case viết hoa chữ C, dấu # dính liền số, một dấu cách sau dấu hai chấm.Xem code tham khảo (C++ / Python)
#include <bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(false); cin.tie(nullptr);
int T;
cin >> T;
for(int t = 1; t <= T; t++){
int n;
cin >> n;
long long mx = LLONG_MIN, mn = LLONG_MAX; // khởi tạo lại cho từng bộ
for(int i = 0; i < n; i++){
long long x;
cin >> x;
mx = max(mx, x);
mn = min(mn, x);
}
cout << "Case #" << t << ": " << mx - mn << "\n";
}
return 0;
}
import sys
data = sys.stdin.buffer.read().split()
T = int(data[0])
p = 1
out = []
for t in range(1, T + 1):
n = int(data[p]); p += 1
a = list(map(int, data[p:p + n])); p += n # lấy đúng n số của bộ này
out.append(f"Case #{t}: {max(a) - min(a)}")
print('\n'.join(out))
File dữ liệu là một đoạn văn bản gồm không quá $1000$ dòng, mỗi dòng không quá $1000$ ký tự (chữ cái, chữ số, dấu cách, dấu câu; có thể có dòng trống). File có thể được soạn trên Windows (xuống dòng là \r\n). Dấu xuống dòng ở cuối dòng cuối cùng (nếu có) không tạo thêm dòng mới. In ra: số dòng; độ dài của dòng dài nhất (không tính ký tự \r, \n); và chính dòng đó (nếu nhiều dòng cùng dài nhất thì in dòng xuất hiện đầu tiên).
Hoc lap trinh
khong kho
chi can kien tri4
16
chi can kien tri📝 Nộp bài, chấm tự động trên ClueOJ ↗
💡 Gợi ý
while(getline(cin, s)), xoá '\r' cuối mỗi dòng rồi so độ dài. Dùng dấu > (không phải >=) để giữ dòng đầu tiên khi bằng nhau. Python đọc nhị phân rồi split(b'\n'), bỏ phần tử rỗng cuối cùng nếu file kết thúc bằng xuống dòng. Mẫu có 4 dòng, dòng thứ ba trống, dòng cuối dài 16 ký tự.Xem code tham khảo (C++ / Python)
#include <bits/stdc++.h>
using namespace std;
int main(){
string s, best;
int cnt = 0;
bool has = false;
while(getline(cin, s)){
if(!s.empty() && s.back() == '\r') s.pop_back();
cnt++;
if(!has || s.size() > best.size()){ best = s; has = true; }
}
cout << cnt << "\n" << best.size() << "\n" << best << "\n";
return 0;
}
import sys
lines = sys.stdin.buffer.read().split(b'\n')
if lines and lines[-1] == b'':
lines.pop() # '\n' cuối file không tạo dòng mới
lines = [x.rstrip(b'\r').decode() for x in lines]
best = lines[0]
for s in lines:
if len(s) > len(best):
best = s
print(len(lines))
print(len(best))
print(best)
Dòng đầu là $n$ ($1 \le n \le 10^4$). Mỗi dòng trong $n$ dòng tiếp theo ghi họ tên một học sinh (một hoặc nhiều từ chỉ gồm chữ cái tiếng Anh, giữa các từ có thể có nhiều dấu cách) và cuối dòng là điểm của bạn đó (số thực từ 0 tới 10, tối đa 2 chữ số sau dấu chấm). In ra họ tên của bạn điểm cao nhất (nếu bằng nhau lấy bạn đứng trước), các từ cách nhau đúng một dấu cách; dòng thứ hai in điểm trung bình của cả lớp với đúng 2 chữ số sau dấu chấm.
3
Nguyen Van An 8.5
Tran Binh 9.25
Le Thi Chau 7Tran Binh
8.25📝 Nộp bài, chấm tự động trên ClueOJ ↗
💡 Gợi ý
stringstream lấy từng từ vào vector, Python dùng line.split() (tự xử lý nhiều dấu cách và '\r'). Trung bình $(8.5 + 9.25 + 7) / 3 = 8.25$, in bằng fixed + setprecision(2).Xem code tham khảo (C++ / Python)
#include <bits/stdc++.h>
using namespace std;
int main(){
int n;
cin >> n;
cin.ignore(numeric_limits<streamsize>::max(), '\n');
string line, bestName;
double best = -1, sum = 0;
for(int i = 0; i < n; i++){
getline(cin, line);
stringstream ss(line);
vector<string> w;
string t;
while(ss >> t) w.push_back(t); // tách từ, bỏ dấu cách thừa và '\r'
double d = stod(w.back()); // từ cuối là điểm
string name = w[0];
for(int k = 1; k + 1 < (int)w.size(); k++) name += " " + w[k];
sum += d;
if(d > best){ best = d; bestName = name; }
}
cout << bestName << "\n";
cout << fixed << setprecision(2) << sum / n << "\n";
return 0;
}
import sys
lines = sys.stdin.buffer.read().decode().split('\n')
n = int(lines[0])
best, best_name, total = -1.0, '', 0.0
for i in range(1, n + 1):
w = lines[i].split() # tách từ, bỏ dấu cách thừa và '\r'
d = float(w[-1]) # từ cuối là điểm
total += d
if d > best:
best, best_name = d, ' '.join(w[:-1])
print(best_name)
print(f"{total / n:.2f}")
Dòng đầu gồm $n$, $m$ ($1 \le n, m \le 500$). Tiếp theo là $n \cdot m$ số nguyên của ma trận theo thứ tự từng hàng, nhưng người soạn đề xuống dòng tuỳ ý (một hàng có thể bị cắt làm nhiều dòng, nhiều hàng có thể nằm chung một dòng). In ra ma trận chuyển vị: $m$ dòng, mỗi dòng $n$ số, các số cách nhau đúng một dấu cách, không có dấu cách thừa cuối dòng.
2 3
1 2
3 4 5
61 4
2 5
3 6📝 Nộp bài, chấm tự động trên ClueOJ ↗
💡 Gợi ý
cin >> không quan tâm xuống dòng, cứ đọc lần lượt $n \cdot m$ số vào a[i][j] là đúng thứ tự. Phần định dạng: hàng $j$ của ma trận mới là cột $j$ của ma trận cũ; in số cuối hàng thì kèm '\n', các số khác kèm dấu cách. Mẫu: ma trận cũ là [[1 2 3], [4 5 6]].Xem code tham khảo (C++ / Python)
#include <bits/stdc++.h>
using namespace std;
int a[505][505];
int main(){
ios::sync_with_stdio(false); cin.tie(nullptr);
int n, m;
cin >> n >> m;
for(int i = 0; i < n; i++)
for(int j = 0; j < m; j++) cin >> a[i][j];
for(int j = 0; j < m; j++)
for(int i = 0; i < n; i++)
cout << a[i][j] << (i + 1 < n ? ' ' : '\n');
return 0;
}
import sys
data = sys.stdin.buffer.read().split()
n, m = int(data[0]), int(data[1])
v = data[2:2 + n * m]
out = []
for j in range(m):
out.append(b' '.join(v[i * m + j] for i in range(n)).decode())
print('\n'.join(out))
Cho dãy $n$ số nguyên $a_1, \ldots, a_n$ ($n \le 5 \cdot 10^5$, $|a_i| \le 10^9$) và $q$ truy vấn ($q \le 5 \cdot 10^5$), mỗi truy vấn gồm $l$, $r$ ($1 \le l \le r \le n$). Với mỗi truy vấn, in tổng $a_l + \ldots + a_r$ trên một dòng. Giới hạn thời gian 1 giây. Dòng đầu là $n$, $q$; dòng hai là dãy số; $q$ dòng sau là các truy vấn.
5 3
1 -2 3 4 5
1 5
2 3
4 411
1
4📝 Nộp bài, chấm tự động trên ClueOJ ↗
💡 Gợi ý
sync_with_stdio(false), cin.tie(nullptr), '\n'; Python đọc một lần bằng sys.stdin.buffer và in một lần bằng join. Dùng endl hoặc print từng dòng là quá thời gian.Xem code tham khảo (C++ / Python)
#include <bits/stdc++.h>
using namespace std;
long long S[500005];
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, q;
cin >> n >> q;
for(int i = 1; i <= n; i++){
long long x;
cin >> x;
S[i] = S[i - 1] + x;
}
while(q--){
int l, r;
cin >> l >> r;
cout << S[r] - S[l - 1] << '\n';
}
return 0;
}
import sys
def main():
data = sys.stdin.buffer.read().split()
n, q = int(data[0]), int(data[1])
S = [0] * (n + 1)
for i in range(n):
S[i + 1] = S[i] + int(data[2 + i])
p = 2 + n
out = []
for _ in range(q):
l, r = int(data[p]), int(data[p + 1]); p += 2
out.append(S[r] - S[l - 1])
sys.stdout.write('\n'.join(map(str, out)) + '\n')
main()
File dữ liệu là một đoạn văn bản tối đa $10^6$ ký tự, gồm nhiều dòng (đọc tới hết file). Một số là một dãy chữ số liên tiếp dài nhất có thể; nếu ngay trước dãy đó là dấu - thì đó là số âm. Ví dụ a-12b chứa số −12; 3-5 chứa hai số 3 và −5; --7 chứa −7; dấu - ở cuối dòng không ảnh hưởng tới số ở đầu dòng sau. Mỗi số có không quá 9 chữ số. In ra số lượng số và tổng của chúng, mỗi giá trị một dòng.
Lop 9A co 35 hoc sinh, nhiet do -2 do.
Nam 2026: diem 8-96
2067📝 Nộp bài, chấm tự động trên ClueOJ ↗
💡 Gợi ý
string s((istreambuf_iterator<char>(cin)), istreambuf_iterator<char>());) để không phải lo xuống dòng, rồi duyệt từng ký tự: gặp chữ số thì gom hết dãy chữ số, xem ký tự ngay trước dãy có phải - không. Python dùng biểu thức chính quy trên dữ liệu nhị phân: re.findall(rb'-?[0-9]+', data). Mẫu có các số 9, 35, −2, 2026, 8, −9, tổng 2067. Tổng có thể tới cỡ $5 \cdot 10^{14}$ nên dùng long long.Xem code tham khảo (C++ / Python)
#include <bits/stdc++.h>
using namespace std;
int main(){
string s((istreambuf_iterator<char>(cin)), istreambuf_iterator<char>()); // đọc toàn bộ file
long long sum = 0;
int cnt = 0, n = s.size(), i = 0;
while(i < n){
if(isdigit((unsigned char)s[i])){
bool neg = (i > 0 && s[i - 1] == '-');
long long v = 0;
while(i < n && isdigit((unsigned char)s[i])){ v = v * 10 + (s[i] - '0'); i++; }
sum += neg ? -v : v;
cnt++;
} else i++;
}
cout << cnt << "\n" << sum << "\n";
return 0;
}
import sys, re
data = sys.stdin.buffer.read() # đọc toàn bộ file dạng nhị phân
nums = [int(x) for x in re.findall(rb'-?[0-9]+', data)]
print(len(nums))
print(sum(nums))
- Không biết học gì trước, học gì sau, học lan man nên kiến thức bị hổng.
- Bí bài không ai gỡ, ngồi cả buổi vẫn không ra rồi bỏ cuộc.
- Code chạy được nhưng không đủ điểm, không biết sai ở đâu, chậm ở đâu.
- Không có đề thi thử và người chấm, nên không biết mình đang ở mức nào.
- Dễ nản giữa chừng khi không có ai theo sát.
- Lộ trình 111 buổi video sắp sẵn từ dòng code đầu tiên đến đề HSG thật của các tỉnh.
- Thầy trực tiếp gỡ bí, chữa bài cho từng em qua nhóm Zalo riêng.
- Hệ thống chấm bài tự động 24/7 và thi thử HSG hằng tuần để biết chính xác mình đang ở đâu.
- Đóng học phí một lần, thầy kèm lâu dài: từ thi chuyên Tin đến HSG quốc gia và cả khi lên đại học.
Nhận xét
Đăng nhận xét