Tour xuôi dòng (Đề tự luyện HSG Tin 12 – Bài 3)
Đề bài
Tour xuôi dòng. Có $N$ bến và $M$ kênh một chiều $u \to v$, không có chu trình. Bến $i$ có độ hấp dẫn $b_i$. Một tour là một đường đi theo các kênh (có thể chỉ gồm một bến), độ hấp dẫn là tổng $b$ của các bến trên đường. Với mỗi bến $x = 1..N$: nếu bến $x$ đóng cửa (tour không được đi qua $x$), độ hấp dẫn lớn nhất của một tour là bao nhiêu? (Không còn tour nào thì đáp án là 0.)
Dữ liệu vào: dòng đầu $N, M$ ($1 \le N \le 2 \cdot 10^5$, $0 \le M \le 2 \cdot 10^5$); dòng hai $b_1, \ldots, b_N$ ($1 \le b_i \le 10^9$); $M$ dòng sau mỗi dòng một kênh $u\ v$ (không trùng kênh, không có chu trình).
Kết quả: một dòng $N$ số, số thứ $x$ là đáp án khi bến $x$ đóng cửa.
- Subtask 1 (30%): $N, M \le 2000$.
- Subtask 2 (30%): mọi kênh có $u < v \le u + 20$.
- Subtask 3 (40%): không có ràng buộc gì thêm.
7 10
9 7 8 4 5 1 2
3 7
6 7
2 6
1 7
2 7
3 5
4 5
4 6
3 4
2 324 17 11 20 22 24 24Tour tốt nhất khi đóng lần lượt bến 1..7: 2→3→4→5 (24); 3→4→5 (17); 1→7 (11); 2→3→5 (20); 2→3→4→6→7 (22); 2→3→4→5 (24); 2→3→4→5 (24).
Bạn nào muốn thử nộp bài để hệ thống chấm xem có đúng hay không thì dùng link này nhé. Bộ test có chia subtask và có cả những test "bẫy", nên code đúng ý tưởng nhưng sai chi tiết nhỏ cũng sẽ bị trừ điểm đó.
Nộp bài Tour xuôi dòngLời mở đầu
Mạng kênh là một đồ thị có hướng không chu trình (DAG), tour là một đường đi. Nếu không bến nào đóng cửa, đây là bài quen thuộc "đường đi có tổng trọng số lớn nhất trên DAG": sắp xếp tô pô rồi quy hoạch động. Cái mới là phải trả lời cho từng bến bị đóng – $N$ câu hỏi, mỗi câu là một đồ thị khác nhau.
Bước 1 – thứ tự tô pô. Đề không hứa kênh luôn đi từ số nhỏ tới số lớn (ví dụ của đề tình cờ như vậy, nhưng test thật thì không). Ta dùng thuật toán Kahn: lần lượt lấy ra các bến không còn kênh nào đi vào. Được dãy ord[0..N-1] mà mọi kênh đều đi từ vị trí nhỏ sang vị trí lớn; pos[v] là vị trí của bến $v$.
Bước 2 – hai mảng QHĐ.
- $f[v]$ = độ hấp dẫn lớn nhất của tour kết thúc ở $v$: $f[v] = b_v + \max(0, \max_{u \to v} f[u])$, tính theo thứ tự
ordxuôi. - $g[v]$ = độ hấp dẫn lớn nhất của tour bắt đầu ở $v$: $g[v] = b_v + \max(0, \max_{v \to w} g[w])$, tính theo thứ tự ngược.
Với ví dụ, thứ tự tô pô là 1, 2, 3, 4, 5, 6, 7 và
| bến | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|---|---|---|---|---|---|---|---|
| b | 9 | 7 | 8 | 4 | 5 | 1 | 2 |
| f | 9 | 7 | 15 | 19 | 24 | 20 | 22 |
| g | 11 | 24 | 17 | 9 | 5 | 3 | 2 |
Bước 3 – nhận xét chìa khóa. Đóng bến $x$ ở vị trí $h$. Đi dọc một tour thì vị trí tăng dần, nên một tour không qua $x$ chỉ có thể là một trong ba loại:
- nằm hẳn bên trái (mọi bến ở vị trí $< h$): tốt nhất là $\text{pre}[h] = \max\{f[\text{ord}[i]] : i < h\}$ – vì $f$ của các bến bên trái chỉ dùng các bến còn ở bên trái hơn, không bao giờ đụng tới $x$;
- nằm hẳn bên phải: tốt nhất là $\text{suf}[h+1] = \max\{g[\text{ord}[i]] : i > h\}$;
- có bến ở cả hai bên: khi đó tour phải đi qua đúng một kênh $u \to w$ với $\text{pos}[u] < h < \text{pos}[w]$ – mình gọi là kênh "bắc qua" vị trí $h$. Tour tốt nhất dùng kênh đó là $f[u] + g[w]$.
Hình 1. Ví dụ của đề, bến 4 đóng cửa. Năm kênh bắc qua vị trí 3 (màu xanh), kênh 3 → 5 cho tour tốt nhất 2 → 3 → 5.
Vậy $$\text{ans}[\text{ord}[h]] = \max\Big(\text{pre}[h],\ \text{suf}[h+1],\ \max_{\text{pos}[u] < h < \text{pos}[w]} \big(f[u] + g[w]\big)\Big).$$ Chú ý dấu nhỏ hơn hẳn ở cả hai đầu: kênh đi vào hoặc đi ra từ chính bến $x$ thì không dùng được. Tổng độ hấp dẫn có thể tới $2 \cdot 10^5 \cdot 10^9$, phải dùng long long.
Cách 1Đóng từng bến, QHĐ lại từ đầu – O(N·(N + M)) · 30%
Chưa cần nhận xét ở bước 3: với mỗi $x$, chạy lại QHĐ $f$ theo thứ tự tô pô nhưng bỏ qua bến $x$ (cho $f[x] = 0$ để không tour nào nối tiếp qua $x$), đáp án là $\max f$. Mỗi lần $O(N + M)$, tổng $O(N \cdot (N + M))$ – khoảng $8 \cdot 10^6$ khi $N, M \le 2000$.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main() {
ios::sync_with_stdio(false); cin.tie(nullptr);
int n, m;
cin >> n >> m;
vector<ll> b(n + 1);
for (int i = 1; i <= n; i++) cin >> b[i];
vector<vector<int>> ra(n + 1), vao(n + 1);
vector<int> deg(n + 1, 0);
for (int j = 0; j < m; j++) {
int u, v; cin >> u >> v;
ra[u].push_back(v); vao[v].push_back(u); deg[v]++;
}
vector<int> ord; // thu tu topo (Kahn)
for (int v = 1; v <= n; v++) if (deg[v] == 0) ord.push_back(v);
for (int h = 0; h < (int)ord.size(); h++)
for (int w : ra[ord[h]]) if (--deg[w] == 0) ord.push_back(w);
vector<ll> f(n + 1);
for (int x = 1; x <= n; x++) { // dong ben x roi QHD lai tu dau
ll best = 0;
for (int v : ord) {
if (v == x) { f[v] = 0; continue; } // f[x] = 0: khong ai noi tiep qua x
f[v] = 0;
for (int u : vao[v]) f[v] = max(f[v], f[u]);
f[v] += b[v];
best = max(best, f[v]);
}
cout << best << (x < n ? ' ' : '\n');
}
return 0;
}import sys
def main():
data = sys.stdin.buffer.read().split()
n, m = int(data[0]), int(data[1])
b = [0] + list(map(int, data[2:2 + n]))
ra = [[] for _ in range(n + 1)]
vao = [[] for _ in range(n + 1)]
deg = [0] * (n + 1)
p = 2 + n
for _ in range(m):
u = int(data[p]); v = int(data[p + 1]); p += 2
ra[u].append(v); vao[v].append(u); deg[v] += 1
ord_ = [v for v in range(1, n + 1) if deg[v] == 0] # thu tu topo (Kahn)
h = 0
while h < len(ord_):
for w in ra[ord_[h]]:
deg[w] -= 1
if deg[w] == 0:
ord_.append(w)
h += 1
f = [0] * (n + 1)
out = []
for x in range(1, n + 1): # dong ben x roi QHD lai tu dau
best = 0
for v in ord_:
if v == x:
f[v] = 0 # f[x] = 0: khong ai noi tiep qua x
continue
mx = 0
for u in vao[v]:
if f[u] > mx:
mx = f[u]
f[v] = mx + b[v]
if f[v] > best:
best = f[v]
out.append(best)
sys.stdout.write(" ".join(map(str, out)) + "\n")
main()Cách 2Cập nhật từng vị trí bị bắc qua – O(N + M + tổng độ dài kênh) · 60%
Dùng công thức ở bước 3. Phần $\text{pre}$, $\text{suf}$ tính trong $O(N)$. Phần kênh bắc qua: với mỗi kênh $u \to w$, duyệt mọi vị trí $h$ từ $\text{pos}[u] + 1$ tới $\text{pos}[w] - 1$ và cập nhật qua[h] = max(qua[h], f[u] + g[w]).
Ở subtask 2, nếu Kahn luôn lấy bến có số hiệu nhỏ nhất trước (dùng priority_queue thay cho hàng đợi thường) thì thứ tự tô pô chính là $1, 2, \ldots, N$ (vì mọi kênh $u < v$), nên mỗi kênh bắc qua tối đa 19 vị trí – rất nhanh. Nhưng ở subtask 3, một kênh có thể bắc qua hàng trăm nghìn vị trí và tổng có thể lên tới $M \cdot N \approx 4 \cdot 10^{10}$.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main() {
ios::sync_with_stdio(false); cin.tie(nullptr);
int n, m;
cin >> n >> m;
vector<ll> b(n + 1);
for (int i = 1; i <= n; i++) cin >> b[i];
vector<int> eu(m), ev(m), deg(n + 1, 0);
vector<vector<int>> ra(n + 1), vao(n + 1);
for (int j = 0; j < m; j++) {
cin >> eu[j] >> ev[j];
ra[eu[j]].push_back(ev[j]); vao[ev[j]].push_back(eu[j]); deg[ev[j]]++;
}
// Kahn, luon lay ben co so hieu nho nhat: o subtask 2 thu tu nay chinh la 1, 2, ..., n
priority_queue<int, vector<int>, greater<int>> q;
for (int v = 1; v <= n; v++) if (deg[v] == 0) q.push(v);
vector<int> ord, pos(n + 1);
while (!q.empty()) {
int u = q.top(); q.pop();
pos[u] = ord.size(); ord.push_back(u);
for (int w : ra[u]) if (--deg[w] == 0) q.push(w);
}
vector<ll> f(n + 1, 0), g(n + 1, 0);
for (int h = 0; h < n; h++) {
int v = ord[h];
for (int u : vao[v]) f[v] = max(f[v], f[u]);
f[v] += b[v];
}
for (int h = n - 1; h >= 0; h--) {
int v = ord[h];
for (int w : ra[v]) g[v] = max(g[v], g[w]);
g[v] += b[v];
}
vector<ll> pre(n + 1, 0), suf(n + 1, 0), qua(n, 0);
for (int h = 0; h < n; h++) pre[h + 1] = max(pre[h], f[ord[h]]);
for (int h = n - 1; h >= 0; h--) suf[h] = max(suf[h + 1], g[ord[h]]);
for (int j = 0; j < m; j++) // kenh u->w bac qua tung vi tri nam giua
for (int h = pos[eu[j]] + 1; h < pos[ev[j]]; h++)
qua[h] = max(qua[h], f[eu[j]] + g[ev[j]]);
vector<ll> ans(n + 1);
for (int h = 0; h < n; h++) ans[ord[h]] = max({pre[h], suf[h + 1], qua[h]});
for (int v = 1; v <= n; v++) cout << ans[v] << (v < n ? ' ' : '\n');
return 0;
}import sys
from heapq import heappush, heappop
def main():
data = sys.stdin.buffer.read().split()
n, m = int(data[0]), int(data[1])
b = [0] + list(map(int, data[2:2 + n]))
eu = list(map(int, data[2 + n:2 + n + 2 * m:2]))
ev = list(map(int, data[3 + n:3 + n + 2 * m:2]))
ra = [[] for _ in range(n + 1)]
vao = [[] for _ in range(n + 1)]
deg = [0] * (n + 1)
for j in range(m):
ra[eu[j]].append(ev[j]); vao[ev[j]].append(eu[j]); deg[ev[j]] += 1
# Kahn, luon lay ben co so hieu nho nhat: o subtask 2 thu tu nay chinh la 1, 2, ..., n
q = [v for v in range(1, n + 1) if deg[v] == 0]
ord_ = []
pos = [0] * (n + 1)
while q:
u = heappop(q)
pos[u] = len(ord_); ord_.append(u)
for w in ra[u]:
deg[w] -= 1
if deg[w] == 0:
heappush(q, w)
f = [0] * (n + 1)
g = [0] * (n + 1)
for v in ord_:
mx = 0
for u in vao[v]:
if f[u] > mx:
mx = f[u]
f[v] = mx + b[v]
for v in reversed(ord_):
mx = 0
for w in ra[v]:
if g[w] > mx:
mx = g[w]
g[v] = mx + b[v]
pre = [0] * (n + 1)
suf = [0] * (n + 1)
for h in range(n):
pre[h + 1] = max(pre[h], f[ord_[h]])
for h in range(n - 1, -1, -1):
suf[h] = max(suf[h + 1], g[ord_[h]])
qua = [0] * n
for j in range(m): # kenh u->w bac qua tung vi tri nam giua
val = f[eu[j]] + g[ev[j]]
for h in range(pos[eu[j]] + 1, pos[ev[j]]):
if val > qua[h]:
qua[h] = val
ans = [0] * (n + 1)
for h in range(n):
ans[ord_[h]] = max(pre[h], suf[h + 1], qua[h])
sys.stdout.write(" ".join(map(str, ans[1:])) + "\n")
main()Cách 3Quét + hàng đợi ưu tiên – O((N + M) log M) · 100%
Mỗi kênh $u \to w$ "phủ" một đoạn vị trí liên tiếp $(\text{pos}[u], \text{pos}[w])$ với giá trị $f[u] + g[w]$; tại mỗi vị trí $h$ ta cần giá trị lớn nhất trong các đoạn đang phủ $h$. Đi $h$ từ trái sang phải (đường quét):
- tới $h = \text{pos}[u] + 1$ thì kênh bắt đầu phủ: đẩy cặp (giá trị, $\text{pos}[w]$) vào một
priority_queuelấy giá trị lớn nhất (mảngbd[h]chứa các kênh bắt đầu phủ tại $h$); - trước khi đọc đỉnh hàng đợi, bỏ đi các phần tử đỉnh đã hết phủ ($\text{pos}[w] \le h$). Phần tử hết hạn nằm sâu bên dưới thì cứ kệ nó – khi nào nó nổi lên đỉnh mới bỏ ("xóa lười").
Mỗi kênh vào và ra hàng đợi đúng một lần, nên phần quét chỉ tốn $O(M \log M)$.
Chạy thử với ví dụ (thứ tự tô pô 1..7, vị trí = số hiệu − 1):
| Bến đóng (vị trí) | pre | suf | Kênh bắc qua: f[u] + g[w] | Đáp án |
|---|---|---|---|---|
| 1 (0) | 0 | 24 | – | 24 |
| 2 (1) | 9 | 17 | 1→7: 9 + 2 = 11 | 17 |
| 3 (2) | 9 | 9 | 1→7: 11; 2→6: 7 + 3 = 10; 2→7: 7 + 2 = 9 | 11 |
| 4 (3) | 15 | 5 | 1→7: 11; 2→6: 10; 2→7: 9; 3→7: 15 + 2 = 17; 3→5: 15 + 5 = 20 | 20 |
| 5 (4) | 19 | 3 | 1→7: 11; 2→7: 9; 3→7: 17; 4→6: 19 + 3 = 22 | 22 |
| 6 (5) | 24 | 2 | 1→7: 11; 2→7: 9; 3→7: 17 | 24 |
| 7 (6) | 24 | 0 | – | 24 |
Các bạn thử nhập mạng kênh của mình (tối đa 10 bến, 20 kênh; số hiệu bến có thể lộn xộn):
- Coi $1, 2, \ldots, N$ là thứ tự xuôi dòng (ví dụ của đề tình cờ đúng như vậy!) – test thật đánh số ngẫu nhiên, phải sắp xếp tô pô.
- Quên loại tour thứ 3 (đi vòng qua bến đóng bằng một kênh bắc qua).
- Lệch biên khi bỏ phần tử khỏi hàng đợi: kênh $u \to w$ với $\text{pos}[w] = h$ đi vào chính bến đóng, phải bỏ – điều kiện là
pos[w] <= h. Ví dụ của đề không bắt được lỗi này. - Dùng
intcho $f$, $g$: tổng tới $2 \cdot 10^{14}$.
Bản Python giống hệt C++: Kahn bằng danh sách, heapq là hàng đợi ưu tiên nhỏ nhất nên mình lưu giá trị âm để lấy lớn nhất.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main() {
ios::sync_with_stdio(false); cin.tie(nullptr);
int n, m;
cin >> n >> m;
vector<ll> b(n + 1);
for (int i = 1; i <= n; i++) cin >> b[i];
vector<int> eu(m), ev(m), deg(n + 1, 0);
vector<vector<int>> ra(n + 1), vao(n + 1);
for (int j = 0; j < m; j++) {
cin >> eu[j] >> ev[j];
ra[eu[j]].push_back(ev[j]);
vao[ev[j]].push_back(eu[j]);
deg[ev[j]]++;
}
// 1. Thu tu topo (Kahn): ord[h] la ben o vi tri h, pos[v] la vi tri cua ben v
vector<int> ord, pos(n + 1);
for (int v = 1; v <= n; v++) if (deg[v] == 0) ord.push_back(v);
for (int h = 0; h < (int)ord.size(); h++) {
int u = ord[h];
pos[u] = h;
for (int w : ra[u]) if (--deg[w] == 0) ord.push_back(w);
}
// 2. f[v]: tour tot nhat KET THUC o v; g[v]: tour tot nhat BAT DAU o v
vector<ll> f(n + 1, 0), g(n + 1, 0);
for (int h = 0; h < n; h++) {
int v = ord[h];
for (int u : vao[v]) f[v] = max(f[v], f[u]);
f[v] += b[v];
}
for (int h = n - 1; h >= 0; h--) {
int v = ord[h];
for (int w : ra[v]) g[v] = max(g[v], g[w]);
g[v] += b[v];
}
// 3. pre[h] = max f cua cac ben o vi tri < h; suf[h] = max g cua cac ben o vi tri >= h
vector<ll> pre(n + 1, 0), suf(n + 1, 0);
for (int h = 0; h < n; h++) pre[h + 1] = max(pre[h], f[ord[h]]);
for (int h = n - 1; h >= 0; h--) suf[h] = max(suf[h + 1], g[ord[h]]);
// 4. Kenh u->w "bac qua" cac vi tri pos[u] < h < pos[w], cho tour f[u] + g[w]
vector<vector<int>> bd(n);
for (int j = 0; j < m; j++)
if (pos[ev[j]] - pos[eu[j]] >= 2) bd[pos[eu[j]] + 1].push_back(j);
priority_queue<pair<ll, int>> pq; // (do hap dan cua tour, vi tri ket thuc pos[w])
vector<ll> ans(n + 1);
for (int h = 0; h < n; h++) {
for (int j : bd[h]) pq.push({f[eu[j]] + g[ev[j]], pos[ev[j]]});
while (!pq.empty() && pq.top().second <= h) pq.pop();
ll best = max(pre[h], suf[h + 1]);
if (!pq.empty()) best = max(best, pq.top().first);
ans[ord[h]] = best;
}
for (int v = 1; v <= n; v++) cout << ans[v] << (v < n ? ' ' : '\n');
return 0;
}import sys
from heapq import heappush, heappop
def main():
data = sys.stdin.buffer.read().split()
n, m = int(data[0]), int(data[1])
b = [0] + list(map(int, data[2:2 + n]))
eu = list(map(int, data[2 + n:2 + n + 2 * m:2]))
ev = list(map(int, data[3 + n:3 + n + 2 * m:2]))
ra = [[] for _ in range(n + 1)]
vao = [[] for _ in range(n + 1)]
deg = [0] * (n + 1)
for j in range(m):
u = eu[j]; v = ev[j]
ra[u].append(v); vao[v].append(u); deg[v] += 1
# 1. Thu tu topo (Kahn)
ord_ = [v for v in range(1, n + 1) if deg[v] == 0]
h = 0
while h < len(ord_):
for w in ra[ord_[h]]:
deg[w] -= 1
if deg[w] == 0:
ord_.append(w)
h += 1
pos = [0] * (n + 1)
for h in range(n):
pos[ord_[h]] = h
# 2. f: tour tot nhat ket thuc o v; g: tour tot nhat bat dau o v
f = [0] * (n + 1)
g = [0] * (n + 1)
for v in ord_:
best = 0
for u in vao[v]:
if f[u] > best:
best = f[u]
f[v] = best + b[v]
for v in reversed(ord_):
best = 0
for w in ra[v]:
if g[w] > best:
best = g[w]
g[v] = best + b[v]
# 3. pre[h] = max f o vi tri < h; suf[h] = max g o vi tri >= h
pre = [0] * (n + 1)
suf = [0] * (n + 1)
for h in range(n):
x = f[ord_[h]]
pre[h + 1] = pre[h] if pre[h] > x else x
for h in range(n - 1, -1, -1):
x = g[ord_[h]]
suf[h] = suf[h + 1] if suf[h + 1] > x else x
# 4. Kenh u->w bac qua vi tri pos[u] < h < pos[w]
bd = [[] for _ in range(n)]
for j in range(m):
pu = pos[eu[j]]; pw = pos[ev[j]]
if pw - pu >= 2:
bd[pu + 1].append((-(f[eu[j]] + g[ev[j]]), pw))
pq = []
ans = [0] * (n + 1)
for h in range(n):
for e in bd[h]:
heappush(pq, e)
while pq and pq[0][1] <= h:
heappop(pq)
best = pre[h] if pre[h] > suf[h + 1] else suf[h + 1]
if pq and -pq[0][0] > best:
best = -pq[0][0]
ans[ord_[h]] = best
sys.stdout.write(" ".join(map(str, ans[1:])) + "\n")
main()Bộ test chấm "bắt" những lỗi nào?
Bộ test có 31 test: 12 test subtask 1 ($N, M \le 2000$, số hiệu bến xáo trộn), 9 test subtask 2 ($N \approx 10^5$, kênh ngắn $u < v \le u + 20$), 10 test subtask 3 ($N, M \approx 2 \cdot 10^5$, số hiệu xáo trộn, khoảng 70% kênh rất dài – mỗi kênh bắc qua hàng chục nghìn vị trí). Có test chỉ một bến, test không có kênh nào, test chỉ là một đường thẳng, test có vài bến trung tâm rất nhiều kênh, test độ hấp dẫn sát $10^9$.
| Cách làm | Sub 1 (30%) | Sub 2 (30%) | Sub 3 (40%) | Điểm |
|---|---|---|---|---|
| Cách 1 – đóng từng bến rồi QHĐ lại | Đúng | TLE | TLE | 30 |
| Cách 2 – cập nhật từng vị trí bị bắc qua | Đúng | Đúng | TLE | 60 |
| Cách 3 – quét + hàng đợi ưu tiên | Đúng | Đúng | Đúng | 100 |
| Sai 1 – chỉ xét tour hẳn bên trái/phải (quên kênh bắc qua) | 3/12 test | 1/9 test | WA | ≈ 11 |
| Sai 2 – lệch biên: kênh kết thúc đúng ở bến đóng vẫn tính | 5/12 test | 1/9 test | 1/10 test | ≈ 20 |
| Sai 3 – coi 1, 2, …, N là thứ tự xuôi dòng | 3/12 test | Đúng | WA | 37,5 |
Sai 4 – dùng int | 5/12 test | 1/9 test | 2/10 test | ≈ 24 |
Ghi chú của mình: các cách C++ ở trên đã được chấm thử với đúng bộ test của bài: Cách 1 đúng 12/12 test subtask 1 và chạy quá 3 giây ở mọi test còn lại; Cách 2 đúng hết subtask 1, 2 và quá 3 giây ở mọi test subtask 3 (đo riêng khoảng 6 giây); Cách 3 đúng 31/31 test, test lâu nhất khoảng 0,3 giây. "Sai 3" được trọn subtask 2 vì ở đó thứ tự 1..N đúng là thứ tự xuôi dòng – nhưng trượt hết subtask 3. Các test mà những lời giải sai khác "ăn may" là ví dụ của đề, test một bến, test không có kênh và test chỉ là một đường thẳng (không có kênh nào bắc qua).
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 😇
- 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