Trung bình cộng 2
Đề bài
Bài 3 (4 điểm): Trung bình cộng
Cho dãy $A$ gồm $n$ số nguyên $a_1, a_2, \ldots, a_n$ và số nguyên $k$. Hãy đếm xem trong dãy có bao nhiêu dãy con liên tiếp có trung bình cộng bằng $k$.
Dữ liệu: vào từ tệp TBC.INP gồm:
- Dòng 1: gồm 2 số $n$ và $k$ $(1 \le n \le 10^5;\ 1 \le k \le 10^9)$.
- Dòng 2: gồm $n$ số nguyên $a_1, a_2, \ldots, a_n$ $(1 \le a_i \le 10^9)$ cách nhau dấu cách.
Kết quả: Ghi ra tệp TBC.OUT gồm một số là kết quả tìm được.
Giới hạn:
- 50% số test ứng với $n \le 10^4$;
- 50% số test ứng với $n \le 10^5$.
Giải thích ví dụ: với $k = 1$, các đoạn có trung bình bằng 1 là 3 đoạn dài 1 $(1)$, 2 đoạn dài 2 $(1, 1)$ và 1 đoạn dài 3 $(1, 1, 1)$ – tổng cộng $6$ đoạn. Mọi đoạn chứa số $2$ đều có trung bình lớn hơn 1.
Lưu ý: trên CLUE OJ bài này đọc/ghi file TBC.INP / TBC.OUT. Code bên dưới tự kiểm tra: có file thì đọc file, không có thì đọc bàn phím.
4 1
1 1 1 26Lời mở đầu
"Trung bình cộng bằng $k$" nghe có vẻ phải chia, phải lo số thực. Nhưng chỉ cần một mẹo nhỏ: trừ $k$ vào mọi phần tử, bài toán biến thành "đếm đoạn con có tổng bằng 0" – một bài kinh điển giải bằng tổng tiền tố + map trong $O(n \log n)$.
Bấm vào nút bên dưới để nộp bài và được hệ thống CLUE OJ chấm tự động.
Nộp bài trên CLUE OJLộ trình lời giải, bấm vào từng phần để nhảy tới:
Bộ testBộ test chính thức có gì?
Bộ test có 20 test:
| Test | n | k lớn nhất | $a_i$ lớn nhất | Tỉ lệ $a_i = k$ | Đáp án |
|---|---|---|---|---|---|
| 1–10 | 15 … 20 | 10 | 9 | 11% | 0 … 6 |
| 11–20 | 100.000 | 10 | 9 | 10% | 0 … 9.751.518 |
Vài điều đáng chú ý khi soi bộ test:
- Nửa đầu (test 1–10) chỉ có $n \le 20$ – nhỏ hơn nhiều so với $n \le 10^4$ đề ghi. Nửa sau (test 11–20) đều có $n = 10^5$.
- Đề cho $a_i, k$ tới $10^9$ nhưng test thật chỉ có $a_i$ từ 1 đến 9 và $k \le 10$. Code vẫn viết theo giới hạn của đề (dùng
map, không dùng mảng đếm theo giá trị). - Test 1, 5, 17 có $k = 10$ lớn hơn mọi $a_i$ nên đáp án bằng 0; test 4, 12 có $k = 1$ nên chỉ những đoạn toàn số 1 mới thoả.
Đối chiếu giới hạn của đề với test thật và số test đúng của từng cách:
| Phần | Đề ghi | Test thật | Cách 1 $O(n^2)$ | Cách 2 map |
|---|---|---|---|---|
| 50% đầu | $n \le 10^4$ | 1–10 | 10/10 | 10/10 |
| 50% sau | $n \le 10^5$ | 11–20 | 0/10 | 10/10 |
int. Tổng tiền tố cũng có thể tới $10^{14}$. Cả hai phải dùng long long.Ý tưởngTrừ k rồi đếm đoạn có tổng 0
Đoạn $a_{i..j}$ (dài $len = j - i + 1$) có trung bình bằng $k$ khi và chỉ khi $$a_i + \ldots + a_j = k \cdot len \iff (a_i - k) + (a_{i+1} - k) + \ldots + (a_j - k) = 0.$$ Đặt $b_t = a_t - k$ và tổng tiền tố $p_j = b_1 + \ldots + b_j$ ($p_0 = 0$). Đoạn $b_{i+1..j}$ có tổng 0 khi và chỉ khi $p_i = p_j$.
map cnt: cnt[t] = số tiền tố $p_0, \ldots, p_{j-1}$ bằng $t$. Tại mỗi $j$: cộng cnt[p_j] vào đáp án, rồi tăng cnt[p_j]. Ban đầu cnt[0] = 1 (ứng với $p_0$).Em nhập dãy và $k$ rồi bấm Tiếp để xem từng tổng tiền tố "bắt cặp" với các tiền tố bằng nó:
Cách 1Thử mọi đoạn – 50% điểm
Thử mọi điểm đầu $i$, kéo dài điểm cuối $j$ và cộng dồn tổng $s$. Đoạn thoả khi $s = k \cdot (j - i + 1)$ – so sánh bằng phép nhân để khỏi phải chia (tránh số thực). Bản Python trừ $k$ trước nên chỉ cần so s == 0.
Kết quả chạy thật: bản C++ đúng 10/20 test (quá thời gian ở test 11–20), test đúng chạy lâu nhất 0.00s; bản Python đúng 10/20 test (quá thời gian ở test 11–20), test đúng lâu nhất 0.02s. Ăn trọn nửa đầu; với $n = 10^5$ phải xét khoảng $5 \cdot 10^9$ đoạn nên cả C++ lẫn Python đều quá thời gian.
Cách 2Tổng tiền tố + map – Full điểm
cnt[0] = 1: tiền tố rỗng $p_0 = 0$, để đếm được các đoạn bắt đầu từ vị trí 1 (ví dụ $a_1 = k$ thì $p_1 = 0 = p_0$).dem += cnt[p]trướccnt[p]++: chỉ ghép $p_j$ với các tiền tố đứng trước nó, không tự ghép với chính nó.- Không cần lưu mảng $a$: đọc tới đâu xử lý tới đó.
- Có thể thay
mapbằngunordered_mapcho nhanh hơn chút, nhưngmap$O(n \log n)$ đã thừa sức.
Kết quả chạy thật: bản C++ đúng 20/20 test, test đúng chạy lâu nhất 0.05s; bản Python đúng 20/20 test, test đúng lâu nhất 0.11s.
Tổng kếtKết quả chạy thật trên bộ test
| Cách | Ngôn ngữ | Đúng | Test đúng lâu nhất | Ghi chú |
|---|---|---|---|---|
| 1. Thử mọi đoạn $O(n^2)$ | C++ | 10/20 | 0.00s | Quá thời gian test 11–20 |
| Python | 10/20 | 0.02s | quá thời gian test 11–20 | |
2. Trừ k + tổng tiền tố + map | C++ | 20/20 | 0.05s | Full điểm |
| Python | 20/20 | 0.11s | Full điểm (dict) |
- 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