# Bài 4
C1 : Xét hết các trường hợp
a + b > 1000 or a + c > 1000 | b + c > 1000 => Yes
else NO
C2 : Xét 2 số lớn nhất
2 số lớn nhất => tổng lớn nhất => đem so sánh với 1000
# Bài 3 : Chênh lệch
Giả sử tồn tại dãy thỏa mãn a1, a2, a3, a4..., am
=> sort lại dãy vẫn thỏa mãn
a[j] - a[i] <= k (a[i] < a[j], i < j)
nếu sort lại => a[i + 1] - a[i] <= k (vì a[i + 1] < a[j])
=> a[j] - a[i] >= a[i + 1] - a[i]
=> Để tối ưu nhất là sort lại
- Nhận xét : Vì ta có thể sắp xếp theo thứ tự tùy ý => sort lại tăng dần
- Gọi dp[i] độ dài lớn nhất kết thúc tại i
- Ban đầu dp[i] = 1 (chính nó)
- Nếu a[i] - a[i - 1] <= k => dp[i] = dp[i - 1] + 1
- lấy max(dp[i]) (1 <= i <= n)
# Bài 1
## Sub1 :
Xét tất cả các cặp, nếu thỏa mãn thì +1 vào đáp án
O($n ^ 2$)
## Dành cho O(n * q * log) :
Nhận xét : Giả sử đang xét tại vị trí i
a[i] + a[j] = B
=> a[j] = B - a[i] (j < i) => Tồn tại 1 giá trị a[j]
a[i] * a[j] = C
=> a[i] * (B - a[i]) = C (1)
Nếu thõa mãn (1) => res += cnt[a[j]] với cnt[x] là số lượng giá trí x trong khoảng từ 1 -> i - 1
Giá trị âm và quá lớn :
=> map<int, int>cnt;
Tóm tắt map<fi, se> : gán kiểu giá trị se cho kiểu giá trị cnt
VD : map<string, int>mp; mp["abc"] = 5
Sau khi xét vị trí i thì cnt[a[i]]++
## Sub cuối :
Chỉ tồn tại 2 giá trị sao cho x + y = A và x * y = B => Giải phương trình bậc 2 tìm x, y
x == y : res += cnt[x] * (cnt[x] - 1) / 2
x != y : res += cnt[x] * cnt[y]
# Bài 2 :
## Sub1 :
Push hết tất cả các giá trị vào 1 vector
Ta xét tất cả các hoán vị đến khi tìm được hoán vị thỏa mãn
``` c++
do {
if(check(v)) {
print(v); Giả dụ
return 0;
}
} while(next_permutation(v.begin(), v.end()));
```
* Lưu ý : Muốn xét hết tất cả các hoán vị thì ta sort lại vector
Gọi id[v] là vị trí hiện tại của của mã số v
- Với a[i] != 0, b[i] != 0 : id[a[i]] + 2 = id[b[i]]
- Với a[i] == 0 : => Thằng ghi tờ giấy i phải ở vị trí 1 => id[b[i]] phải bằng 1
- Với b[i] == 0 : => Thằng ghi tờ giấy i phải ở vị trí n => id[a[i]] = n - 1
=> $O(n!)$
## Sub 2 :
Gọi pos[v] : là chỉ số cuối cùng của thằng có mã số v
- le[i] : mã số của thằng có pos = pos[i] - 2
- ri[i] : mã số của thằng có pos = pos[i] + 2
- Ban đầu ta gán le[i] = ri[i] = -1 với mọi i
- Với mọi tờ giấy thì ta gán : ri[a[i]] = b[i], le[b[i]] = a[i]
Nếu le[v] = -1 => v ở vị trí 1 => pos[v] = 1
=> pos[ri[v]] = 3, pos[ri[ri[v]]] = 5, ...
Nếu le[v] = 0 => pos[v] = 2
=> pos[ri[v]] = 4, pos[ri[ri[v]]] = 6, ...
# Bài 5 :
## Sub 1 :
Sau 1 lần chơi thì độ dài xâu s giảm đi 2
Gọi d là số lần chơi
- Nếu d lẻ => Bob thắng
- Nếu d chẵn => Alice thắng
d = n / 2 (lấy phần nguyên)
# Sub 2 :
Không hề có chiến thuật để thắng
=> Thấy cặp thỏa mãn thì người chơi xóa và đến phiên người còn lại
Ta tìm cặp thõa mãn đầu tiên trên sâu s
=> Tìm i nhỏ nhất thỏa mãn s[i] == s[i + 1]
- le[i] : vị trí ký tự bên trái của xâu mà chưa bị xóa
- ri[i] : vị trí ký tự bên phải của xâu mà chưa bị xóa
Giả sử ta xóa kí tự i :
Đặt current_left = le[i], current_right = ri[i]
=> le[current_right] = current_left
=> ri[current_left] = current_right
Vì ta đang xét vị trí nhỏ nhất thõa mãn
=> Ban đầu khi chưa ký tự nào bị xóa ta push hết các vị trí i sao cho $s[i] = s[i + 1]$ vào một priority_queue
priority_queue<int>pq (sort giá trị)
pq.push(1)
pq.push(3)
pq.push(2)
=> pq.top() = 3
Muốn xóa giá trị trên cùng : pq.pop()
Muốn giá trị trên cùng là nhỏ nhất
=> Khai báo priority_queue<int, vector<int>, greater<int>>pq
Gọi del[i] là trạng thái của vị trí i đã bị xóa chưa
```c++
while(!pq.empty()) {
int id = pq.top(); pq.pop();
if(del[id]) continue;
d++;
del[id] = true;
del[ri[id]] = true;
// Hiện tại muốn xóa thằng id và ri[id]
int c_le = le[id], c_ri = ri[ri[id]];
ri[c_le] = c_ri; le[c_ri] = c_le;
if(c_le > 0 && c_ri < s.size() && s[c_le] == s[c_ri]) pq.emplace(c_le);
}
```
Nếu d lẻ : Bob thắng, còn không thì Alice thắng
# Bài 6 :
## Sub 1 :
Xét hết tất cả các hoán vị thứ tự của ăn của người khách
Nếu tại vị trí i người khách đó không thể nào ăn được vị đã hết hạn thì res = max(res, i)
vector ban đầu chứa các số từ 1 đên n
```c++
int val(vector<int>& v) {
int day = 0;
for(int i = 0; i < n; i++) {
int id = v[i];
if(day > b[i]) return i;
day = max(day + 1, a[i] + 1);
}
return n;
}
```
## Sub 3 :
Tại ngày day tồn tại những nem đã lên men mà chưa bị thối rữa
=> Ta chọn thằng sẽ thối rữa sớm nhất => chọn b bé nhất hiện tại
```c++
priority_queue<int, vector<int>, greater<int>>pq;
for(int day = 1; day <= 1e6; day++) {
while(!pq.empty() && pq.top() < day) pq.pop();
for(int& x : event[day]) pq.emplace(x); // x = b[i]
if(!pq.empty()) res++, pq.pop();
}
cout << res;
```