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
Thi thử miễn phí
🏆 Thi thử HSG Tin học tuần này trên ClueOJ
Mở công khai cho mọi người · đề do thầy Nguyễn Việt Nam Sơn biên soạn
Lịch thi, đề cũ & lời giải →

Bài 11: STL cơ bản trong C++ và Python: vector, stack, queue, set, map (kèm bài kiểm tra dấu ngoặc, Josephus)

Bài 11 – STL: vector, set, map, stack, queue
Series: Nhập môn lập trình thuật toán Độ khó: Dễ Bài 11 / Kiến thức nền tảng STL cơ bản
// Nhập môn lập trình · Bài 11
STL cơ bản trong C++ (đối chiếu Python): vector, stack, queue, set, map
Hộp đồ nghề có sẵn của C++: chỉ vài dòng code là có ngăn xếp kiểm tra dấu ngoặc, hàng đợi mô phỏng xếp hàng, tập hợp loại phần tử trùng, bảng đếm tần suất.

Lời mở đầu

Đến giờ, mọi thứ ta đều tự cài đặt bằng mảng. Điều đó rất tốt để hiểu bản chất, nhưng khi thi, tự viết lại những thứ quen thuộc vừa mất thời gian vừa dễ sai. C++ có sẵn STL (Standard Template Library): một thư viện các cấu trúc dữ liệu và hàm đã được tối ưu, dùng chỉ bằng một dòng #include <bits/stdc++.h>. Bài này giới thiệu 5 “món đồ nghề” dùng nhiều nhất: vector, stack, queue, set, map. Với mỗi món, em sẽ học nó lưu dữ liệu ra sao, các thao tác chính và độ phức tạp của chúng (đã học ở bài 9), kèm cách viết tương ứng trong Python.

Trong bài này bạn sẽ học: ôn vector và các hàm hay dùng; stack (ngăn xếp, vào sau ra trước) và bài kiểm tra dãy ngoặc đúng; queue (hàng đợi, vào trước ra trước) và bài toán Josephus; set (tập hợp tự sắp xếp, không trùng lặp); map (từ điển khóa → giá trị) để đếm tần suất. Có khung chạy tay cho từng cấu trúc, mô phỏng “thử ngay” cả 4 cấu trú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 1vector: mảng tự co giãn

Ta đã làm quen vector ở bài 6. Đây là bảng tóm tắt các thao tác hay dùng nhất, đối chiếu với list của Python. Hãy để ý cột độ phức tạp: thêm/xóa ở cuối rất nhanh $O(1)$, còn thêm/xóa ở giữa phải dịch cả đoạn phía sau nên mất $O(n)$, giống hệt mảng tự cài đặt.

Việc cần làmC++ (vector<int> v)Python (list v)Độ phức tạp
Thêm vào cuốiv.push_back(x)v.append(x)$O(1)$
Xóa phần tử cuốiv.pop_back()v.pop()$O(1)$
Phần tử đầu / cuốiv.front(), v.back()v[0], v[-1]$O(1)$
Số phần tử, kiểm tra rỗngv.size(), v.empty()len(v), not v$O(1)$
Chèn / xóa ở vị trí kv.insert(v.begin() + k, x), v.erase(v.begin() + k)v.insert(k, x), v.pop(k)$O(n)$
Sắp xếp, đảo ngượcsort(v.begin(), v.end()), reverse(…)v.sort(), v.reverse()$O(n \log n)$, $O(n)$
Duyệt mọi phần tửfor(int x : v)for x in v:$O(n)$
Code C++ và Python
#include <bits/stdc++.h>
using namespace std;

int main(){
    vector<int> v;                  // rỗng
    int n; cin >> n;
    for(int i = 0; i < n; i++){ int x; cin >> x; v.push_back(x); }
    sort(v.begin(), v.end());
    for(int x : v) cout << x << " ";        // duyệt kiểu "for-each"
    cout << "\nMin = " << v.front() << ", Max = " << v.back();
    vector<int> w(5, 0);            // vector 5 phần tử, đều bằng 0
    vector<vector<int>> g(3, vector<int>(4));   // ma trận 3 x 4 (bài 7)
    return 0;
}
n = int(input())
v = list(map(int, input().split()))
v.sort()
print(*v)
print("Min =", v[0], ", Max =", v[-1])
w = [0] * 5
g = [[0] * 4 for _ in range(3)]

Phần 2stack: ngăn xếp, vào sau ra trước

Hãy tưởng tượng một chồng đĩa: đĩa đặt vào sau cùng nằm trên đỉnh, và khi lấy ra cũng chỉ lấy được đĩa trên đỉnh. Đó là stack (ngăn xếp), nguyên tắc LIFO (Last In, First Out: vào sau, ra trước). Ba thao tác chính đều $O(1)$: push(x) đặt lên đỉnh, top() xem đỉnh, pop() bỏ đỉnh. Nút “Quay lại” (Undo) của trình soạn thảo, ngăn xếp lời gọi hàm đệ quy ở bài 5 đều hoạt động theo kiểu stack.

stack (LIFO) 3 8 5 ← top(): chỉ xem / lấyđược phần tử trên đỉnh queue (FIFO) 3 8 5 ↑ front(), pop(): ra ở đầupush(): vào ở cuối ↑ Cùng đẩy vào 3, 8, 5: stack lấy ra 5 trước, queue lấy ra 3 trước.

Bài kinh điển: dãy ngoặc đúng. Cho xâu chỉ gồm các dấu ( ) [ ] { }. Xâu là “đúng” nếu mỗi dấu mở đều được đóng bằng đúng loại, theo đúng thứ tự lồng nhau, ví dụ ([]{}) đúng còn ([)] sai. Ý tưởng: duyệt từng ký tự; gặp dấu mở thì push vào stack; gặp dấu đóng thì dấu mở gần nhất chưa được đóng phải nằm ở đỉnh stack và phải cùng loại.

Code C++ và Python
#include <bits/stdc++.h>
using namespace std;

int main(){
    string s;
    cin >> s;
    stack<char> st;
    bool ok = true;
    for(char c : s){
        if(c == '(' || c == '[' || c == '{'){
            st.push(c);
        } else {
            if(st.empty()){ ok = false; break; }
            char m = st.top();
            if((c == ')' && m != '(') || (c == ']' && m != '[') || (c == '}' && m != '{')){
                ok = false; break;
            }
            st.pop();
        }
    }
    if(!st.empty()) ok = false;
    cout << (ok ? "YES" : "NO");
    return 0;
}
s = input().strip()
st = []
cap = {')': '(', ']': '[', '}': '{'}
ok = True
for c in s:
    if c in '([{':
        st.append(c)                # push
    else:
        if not st or st[-1] != cap[c]:
            ok = False
            break
        st.pop()                    # pop
if st:
    ok = False
print("YES" if ok else "NO")
C++▶ Chạy tay từng dòng codeKiểm tra dãy ngoặc đúng bằng stack: dấu mở thì push, dấu đóng thì so với đỉnh stack rồi pop.
Dữ liệu mẫu:
1#include <bits/stdc++.h>
2using namespace std;
3
4int main(){
5 string s;
6 cin >> s;
7 stack<char> st;
8 bool ok = true;
9 for(char c : s){
10 if(c == '(' || c == '[' || c == '{'){
11 st.push(c);
12 } else {
13 if(st.empty()){ ok = false; break; }
14 char m = st.top();
15 if((c == ')' && m != '(') || (c == ']' && m != '[') || (c == '}' && m != '{')){
16 ok = false; break;
17 }
18 st.pop();
19 }
20 }
21 if(!st.empty()) ok = false;
22 cout << (ok ? "YES" : "NO");
23 return 0;
24}

Mô phỏng trực quan. Vì sao kiểm tra dấu ngoặc lại cần đúng một cái stack? Em bấm Tiếp để xem stack lớn lên khi gặp ngoặc mở, co lại khi gặp ngoặc đóng; rồi thử các dãy sai như ([)], ((), ()) để thấy ba kiểu sai khác nhau được phát hiện ở đâu.

MÔ PHỎNGKiểm tra dãy ngoặc đúng bằng stackNhập dãy tối đa 24 dấu ngoặc ( ) [ ] { }. Stack vẽ nằm ngang, đỉnh ở bên phải.
ký tự đang xétđỉnh stackđã xử lýchỗ phát hiện sai

Phần 3queue: hàng đợi, vào trước ra trước

queue (hàng đợi) giống hàng người xếp mua vé: ai đến trước được phục vụ trước, nguyên tắc FIFO (First In, First Out). Các thao tác đều $O(1)$: push(x) thêm vào cuối hàng, front() xem người đầu hàng, pop() cho người đầu hàng đi. Trong Python không nên dùng list.pop(0) (mất $O(n)$) mà dùng collections.deque với popleft(). Queue là trái tim của thuật toán tìm đường đi ngắn nhất BFS mà em sẽ học sau này.

Bài toán Josephus. $n$ người đứng thành vòng tròn, đánh số 1 tới $n$. Bắt đầu đếm từ người số 1, cứ đếm đến người thứ $k$ thì người đó bị loại, rồi đếm tiếp từ người kế bên. Hỏi thứ tự bị loại? Dùng queue: mỗi lần đếm, người đầu hàng đi xuống cuối hàng (pop rồi push lại), đến người thứ $k$ thì pop hẳn.

Code C++ và Python
#include <bits/stdc++.h>
using namespace std;

int main(){
    int n, k;
    cin >> n >> k;
    queue<int> q;
    for(int i = 1; i <= n; i++) q.push(i);
    while(!q.empty()){
        for(int t = 1; t < k; t++){
            q.push(q.front());
            q.pop();
        }
        cout << q.front() << " ";
        q.pop();
    }
    return 0;
}
from collections import deque
n, k = map(int, input().split())
q = deque(range(1, n + 1))
kq = []
while q:
    q.rotate(-(k - 1))         # đưa k-1 người đầu xuống cuối hàng
    kq.append(q.popleft())     # người thứ k bị loại
print(*kq)
C++▶ Chạy tay từng dòng codeJosephus bằng queue: đếm 1 người thì người đó chuyển từ đầu xuống cuối hàng, đếm đến k thì người đầu hàng bị loại.
Dữ liệu mẫu:
1#include <bits/stdc++.h>
2using namespace std;
3
4int main(){
5 int n, k;
6 cin >> n >> k;
7 queue<int> q;
8 for(int i = 1; i <= n; i++) q.push(i);
9 while(!q.empty()){
10 for(int t = 1; t < k; t++){
11 q.push(q.front());
12 q.pop();
13 }
14 cout << q.front() << " ";
15 q.pop();
16 }
17 return 0;
18}

Mô phỏng trực quan. Bài Josephus dùng queue thay cho vòng tròn: ai chưa bị loại thì đi từ đầu xuống cuối hàng. Em nhập n, k rồi bấm Tiếp để xem từng lần pop – push và tự kiểm tra lại kết quả chạy tay của mình.

MÔ PHỎNGBài toán Josephus bằng queueNhập số người n (1 đến 10) và bước đếm k (1 đến 10).
đầu queue, đang được đếmvừa chuyển xuống cuốibị loạingười sống sót

Phần 4set: tập hợp không trùng lặp, tự sắp xếp

set lưu các phần tử khác nhau và luôn giữ chúng theo thứ tự tăng dần. Thêm một phần tử đã có thì set không thay đổi. Mọi thao tác thêm (insert), xóa (erase), kiểm tra có hay không (count hoặc find) đều mất $O(\log n)$, nhanh hơn hẳn việc dò tuần tự $O(n)$ trên mảng. Python cũng có set, nhưng không tự sắp xếp (khi cần thứ tự thì dùng sorted(s)).

Code C++ và Python
#include <bits/stdc++.h>
using namespace std;

int main(){
    int n;
    cin >> n;
    set<int> s;
    for(int i = 0; i < n; i++){
        int x; cin >> x;
        s.insert(x);
    }
    cout << s.size() << "\n";
    for(int x : s) cout << x << " ";
    return 0;
}
n = int(input())
a = list(map(int, input().split()))
s = set(a)
print(len(s))
print(*sorted(s))
C++▶ Chạy tay từng dòng codeThêm từng số vào set: số mới được chèn vào đúng chỗ để set luôn tăng dần, số đã có thì bị bỏ qua.
Dữ liệu mẫu:
1#include <bits/stdc++.h>
2using namespace std;
3
4int main(){
5 int n;
6 cin >> n;
7 set<int> s;
8 for(int i = 0; i < n; i++){
9 int x; cin >> x;
10 s.insert(x);
11 }
12 cout << s.size() << "\n";
13 for(int x : s) cout << x << " ";
14 return 0;
15}
Các thao tác khác của set
set<int> s = {5, 1, 9};
s.insert(3);                        // {1, 3, 5, 9}
s.erase(5);                         // {1, 3, 9}
if(s.count(3)) cout << "co 3\n";    // count trả về 1 nếu có, 0 nếu không
cout << *s.begin() << " " << *s.rbegin();   // phần tử nhỏ nhất, lớn nhất: 1 9
multiset<int> ms = {2, 2, 7};       // multiset: giống set nhưng cho phép trùng
s = {5, 1, 9}
s.add(3)
s.discard(5)          # xóa, không báo lỗi nếu không có
if 3 in s:
    print("co 3")
print(min(s), max(s))

Phần 5map: từ điển khóa → giá trị

map lưu các cặp khóa → giá trị, giống quyển từ điển: tra “khóa” (từ) để lấy “giá trị” (nghĩa). Khác mảng chỉ dùng chỉ số $0..n-1$, khóa của map có thể là bất kỳ: số rất lớn như $10^9$, số âm, xâu ký tự… Ứng dụng số 1 là đếm tần suất: dem[x]++. Truy cập một khóa chưa có thì map tự tạo khóa đó với giá trị 0. Các khóa được giữ tăng dần, thao tác mất $O(\log n)$. Python dùng dict (hoặc collections.Counter để đếm).

Code C++ và Python
#include <bits/stdc++.h>
using namespace std;

int main(){
    int n;
    cin >> n;
    map<string, int> dem;
    for(int i = 0; i < n; i++){
        string w; cin >> w;
        dem[w]++;
    }
    for(auto p : dem)
        cout << p.first << " " << p.second << "\n";
    return 0;
}
from collections import Counter
n = int(input())
dem = Counter(input().split())
for w in sorted(dem):
    print(w, dem[w])
C++▶ Chạy tay từng dòng codeĐếm số lần xuất hiện của mỗi từ bằng map: dem[w]++ tự tạo khóa mới với giá trị 0 rồi tăng lên.
Dữ liệu mẫu:
1#include <bits/stdc++.h>
2using namespace std;
3
4int main(){
5 int n;
6 cin >> n;
7 map<string, int> dem;
8 for(int i = 0; i < n; i++){
9 string w; cin >> w;
10 dem[w]++;
11 }
12 for(auto p : dem)
13 cout << p.first << " " << p.second << "\n";
14 return 0;
15}
Chọn cấu trúc nào?
Cần thêm/lấy ở cuối, truy cập theo chỉ số → vector. Cần “lấy cái vừa cho vào” (ngoặc, hoàn tác, đệ quy bằng tay) → stack. Cần “ai đến trước xử lý trước” (xếp hàng, BFS) → queue. Cần loại trùng, kiểm tra có/không thật nhanh, luôn có thứ tự → set. Cần gắn mỗi khóa với một giá trị (đếm tần suất, tra cứu tên → điểm) → map. Nếu khóa chỉ là số nhỏ (ví dụ $\le 10^6$) thì một mảng đếm thường còn nhanh hơn map, xem bài 13.

Thử ngay: chọn một cấu trúc, gõ giá trị và bấm các nút thao tác để thấy dữ liệu thay đổi thế nào. Dòng nhật ký ghi lại câu lệnh C++ tương ứng với mỗi thao tác.

MÔ PHỎNGThử các cấu trúc dữ liệu STLChọn cấu trúc, gõ giá trị (với map: khóa và giá trị) rồi bấm nút thao tác.
Đang tải mô phỏng…

Bài tậpLuyện STL

Mỗi bài hãy viết bằng cấu trúc STL thích hợp, rồi tự hỏi: nếu chỉ dùng mảng và vòng lặp thì độ phức tạp là bao nhiêu? Giới hạn chung $n, q \le 10^5$.

Bài 1Đảo ngược dãyDễ

Cho dãy $n$ số, in dãy theo thứ tự ngược lại. Hãy làm bằng stack.

Input
5
1 2 3 4 5
Output
5 4 3 2 1
💡 Gợi ý
Push lần lượt các số vào stack, rồi trong khi stack chưa rỗng thì in top() và pop(). Số vào sau cùng ra trước tiên nên thứ tự bị đảo ngược.
Xem code tham khảo (C++ / Python)
#include <bits/stdc++.h>
using namespace std;

int main(){
    int n;
    cin >> n;
    stack<int> st;
    for(int i = 0; i < n; i++){ int x; cin >> x; st.push(x); }
    while(!st.empty()){
        cout << st.top() << " ";
        st.pop();
    }
    return 0;
}
n = int(input())
st = list(map(int, input().split()))
out = []
while st:
    out.append(st.pop())
print(*out)
Bài 2Nhiều dãy ngoặcDễ

Dòng đầu là $t$, mỗi dòng sau là một xâu gồm các dấu ngoặc ()[]{}. Với mỗi xâu in YES nếu là dãy ngoặc đúng, ngược lại in NO.

Input
3
([]{})
([)]
((
Output
YES
NO
NO
💡 Gợi ý
Viết đoạn kiểm tra ở phần 2 thành một hàm bool dung(string s) rồi gọi $t$ lần. Nhớ tạo stack mới (rỗng) cho mỗi xâu.
Xem code tham khảo (C++ / Python)
#include <bits/stdc++.h>
using namespace std;

bool dung(const string &s){
    stack<char> st;
    for(char c : s){
        if(c == '(' || c == '[' || c == '{') st.push(c);
        else {
            if(st.empty()) return false;
            char m = st.top(); st.pop();
            if((c == ')' && m != '(') || (c == ']' && m != '[') || (c == '}' && m != '{')) return false;
        }
    }
    return st.empty();
}

int main(){
    int t;
    cin >> t;
    while(t--){
        string s; cin >> s;
        cout << (dung(s) ? "YES" : "NO") << "\n";
    }
    return 0;
}
def dung(s):
    st, cap = [], {')': '(', ']': '[', '}': '{'}
    for c in s:
        if c in '([{':
            st.append(c)
        elif not st or st.pop() != cap[c]:
            return False
    return not st

t = int(input())
for _ in range(t):
    print("YES" if dung(input().strip()) else "NO")
Bài 3Đếm giá trị phân biệtDễ

Cho dãy $n$ số nguyên ($|a_i| \le 10^9$). Đếm số giá trị khác nhau.

Input
7
4 1 4 2 1 9 4
Output
4
💡 Gợi ý
Cho tất cả vào set rồi in s.size(), độ phức tạp $O(n \log n)$. Không dùng được mảng đánh dấu vì giá trị tới $10^9$ (và có thể âm). Python: len(set(a)).
Xem code tham khảo (C++ / Python)
#include <bits/stdc++.h>
using namespace std;

int main(){
    int n;
    cin >> n;
    set<int> s;
    for(int i = 0; i < n; i++){ int x; cin >> x; s.insert(x); }
    cout << s.size();
    return 0;
}
n = int(input())
print(len(set(map(int, input().split()))))
Bài 4Số xuất hiện nhiều nhấtDễ

Cho dãy $n$ số nguyên. In số xuất hiện nhiều lần nhất và số lần xuất hiện của nó; nếu có nhiều số cùng nhiều nhất, in số nhỏ nhất.

Input
8
3 1 3 2 1 3 2 2
Output
2 3
💡 Gợi ý
Đếm bằng map<int, int> dem. Duyệt map theo thứ tự khóa tăng dần, chỉ cập nhật khi số lần lớn hơn hẳn kỷ lục hiện tại, như vậy khi bằng nhau sẽ giữ lại số nhỏ hơn.
Xem code tham khảo (C++ / Python)
#include <bits/stdc++.h>
using namespace std;

int main(){
    int n;
    cin >> n;
    map<int, int> dem;
    for(int i = 0; i < n; i++){ int x; cin >> x; dem[x]++; }
    int best = 0, sl = 0;
    for(auto p : dem){
        if(p.second > sl){ sl = p.second; best = p.first; }
    }
    cout << best << " " << sl;
    return 0;
}
from collections import Counter
n = int(input())
dem = Counter(map(int, input().split()))
best = min(dem, key=lambda x: (-dem[x], x))
print(best, dem[best])
Bài 5Thống kê từDễ

Cho $n$ và $n$ từ. In mỗi từ khác nhau kèm số lần xuất hiện, theo thứ tự từ điển.

Input
8
the cat and the hat and the bat
Output
and 2
bat 1
cat 1
hat 1
the 3
💡 Gợi ý
Chính là khung chạy tay ở phần 5. map<string, int> tự giữ các khóa theo thứ tự từ điển nên chỉ cần duyệt và in.
Xem code tham khảo (C++ / Python)
#include <bits/stdc++.h>
using namespace std;

int main(){
    int n;
    cin >> n;
    map<string, int> dem;
    for(int i = 0; i < n; i++){ string w; cin >> w; dem[w]++; }
    for(auto p : dem) cout << p.first << " " << p.second << "\n";
    return 0;
}
from collections import Counter
n = int(input())
dem = Counter(input().split())
for w in sorted(dem):
    print(w, dem[w])
Bài 6JosephusTrung bình

Cho $n, k$ ($n, k \le 1000$). In thứ tự bị loại trong trò chơi Josephus ở phần 3.

Input
7 3
Output
3 6 2 7 5 1 4
💡 Gợi ý
Mô phỏng bằng queue: mỗi lượt đưa $k - 1$ người từ đầu xuống cuối hàng rồi loại người đầu hàng. Tổng cộng khoảng $n \cdot k = 10^6$ thao tác, đủ nhanh.
Xem code tham khảo (C++ / Python)
#include <bits/stdc++.h>
using namespace std;

int main(){
    int n, k;
    cin >> n >> k;
    queue<int> q;
    for(int i = 1; i <= n; i++) q.push(i);
    while(!q.empty()){
        for(int t = 1; t < k; t++){ q.push(q.front()); q.pop(); }
        cout << q.front() << " ";
        q.pop();
    }
    return 0;
}
from collections import deque
n, k = map(int, input().split())
q = deque(range(1, n + 1))
kq = []
while q:
    q.rotate(-(k - 1))
    kq.append(q.popleft())
print(*kq)
Bài 7Có hai số tổng bằng x?Trung bình

Cho dãy $n$ số nguyên và số $x$. Có tồn tại hai chỉ số $i \lt j$ với $a_i + a_j = x$ không? In YES hoặc NO.

Input
5 9
2 7 11 15 1
Output
YES
💡 Gợi ý
Duyệt từ trái sang phải, giữ một set chứa các số đã gặp. Tại $a_j$, nếu $x - a_j$ đã có trong set thì có cặp. Rồi mới thêm $a_j$ vào set (để không ghép một số với chính nó). Độ phức tạp $O(n \log n)$ thay vì $O(n^2)$.
Xem code tham khảo (C++ / Python)
#include <bits/stdc++.h>
using namespace std;

int main(){
    int n;
    long long x;
    cin >> n >> x;
    set<long long> da;
    bool co = false;
    for(int i = 0; i < n; i++){
        long long a; cin >> a;
        if(da.count(x - a)) co = true;
        da.insert(a);
    }
    cout << (co ? "YES" : "NO");
    return 0;
}
n, x = map(int, input().split())
a = list(map(int, input().split()))
da, co = set(), False
for v in a:
    if x - v in da:
        co = True
    da.add(v)
print("YES" if co else "NO")
Bài 8Quản lý tập hợpTrung bình

Ban đầu tập hợp rỗng. Có $q$ truy vấn, mỗi truy vấn một dòng: 1 x thêm $x$, 2 x xóa $x$ (nếu có), 3 x hỏi $x$ có trong tập không (in YES/NO).

Input
6
1 5
1 3
3 5
2 5
3 5
3 3
Output
YES
NO
YES
💡 Gợi ý
Dùng set: insert, erase, count, mỗi truy vấn $O(\log n)$. Nếu dùng mảng dò tuần tự thì mỗi truy vấn mất $O(n)$, tổng $10^{10}$ → TLE.
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 q;
    cin >> q;
    set<int> s;
    while(q--){
        int t, x; cin >> t >> x;
        if(t == 1) s.insert(x);
        else if(t == 2) s.erase(x);
        else cout << (s.count(x) ? "YES" : "NO") << "\n";
    }
    return 0;
}
import sys
data = sys.stdin.read().split()
q = int(data[0])
s, out = set(), []
for i in range(q):
    t, x = int(data[1 + 2 * i]), int(data[2 + 2 * i])
    if t == 1:
        s.add(x)
    elif t == 2:
        s.discard(x)
    else:
        out.append("YES" if x in s else "NO")
print('\n'.join(out))
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 😇
Bản quyền thuộc về Thầy Nguyễn Việt Nam Sơn. Bài viết, lời giải, hình minh hoạ và mã nguồn trên trang là công sức biên soạn của thầy qua nhiều năm giảng dạy. Vui lòng không sao chép, đăng lại hay sử dụng dưới mọi hình thức khi chưa được sự đồng ý của tác giả. Nếu thấy hữu ích, hãy chia sẻ bằng đường link bài gốc nhé. Cảm ơn bạn!
Tự học thuật toán: khó ở đâu?
Đọc xong một bài giải là bước đầu. Phần lớn học sinh tự học sẽ gặp những trở ngại sau:
  • 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.
Học cùng thầy Nam Sơn, các trở ngại đó được gỡ bỏ:
  • 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