You are on page 1of 71

CSES Solution

Introductory Problems
Weird Algorithm

Soluion:

Chạy vòng lặp bắt đầu từ số n và kết thúc khi n = 1. Nếu n là số chẵn, ta chia n cho 2; nếu n là số lẻ,
ta nhân n lên 3 và cộng 1

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n; cin >> n;
while (n != 1){
cout << n << " ";
if (n % 2 == 0) n /= 2;
else n = n * 3 + 1;
}
cout << 1;
return 0;
}

Missing Number
Solution:

Đếm số lần xuất hiện của các phần tử của dãy a, sau đó chạy vòng lặp từ 1 đến n, nếu có số nào
không xuất hiện trong dãy a thì in ra.

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n; cin >> n;
int a[n - 1];
unordered_map<int, int> m;
for (int &i : a) cin >> i, m[i]++;
sort(a, a + n - 1);
for (int i = 1; i <= n; i++) if (!m[i]){cout << i; return 0;}
return 0;
}

Repetitions
Solution:

Duyệt qua từng ký tự trong chuỗi:

So sánh ký tự hiện tại với ký tự trước đó:

Nếu khác nhau, thì so sánh độ dài của dãy con hiện tại với độ dài của dãy con liên tiếp dài nhất và
cập nhật độ dài lớn nhất nếu cần, đặt độ dài của dãy con hiện tại về 1 để bắt đầu đếm lại cho dãy
con tiếp theo.

Nếu giống nhau, thì tăng độ dài của dãy con hiện tại lên 1 để tiếp tục đếm.

Kiểm tra xem độ dài của dãy con cuối cùng có lớn hơn max_length không.

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
string s; cin >> s;
int d = 0, res = 0;
char c = '.';
for (char i : s){
if (i != c) res = max(res, d), d = 0, c = i;
d++;
}
res = max(d, res);
cout << res;
return 0;
}

Increasing Array

Solution:

Duyệt qua mảng từ phần tử thứ hai đến cuối cùng.

So sánh a[i] với a[i - 1]:

Nếu a[i] nhỏ hơn a[i - 1], thì cộng sự khác biệt giữa chúng vào res và cập nhật a[i] thành a[i - 1] để
đảm bảo tính không giảm của mảng.

In ra giá trị res.

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long


signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n, res = 0; cin >> n;
int a[n];
for (int &i : a) cin >> i;
for (int i = 1; i < n; i++){
if (a[i] < a[i - 1]) res += a[i - 1] - a[i], a[i] = a[i - 1];
}
cout << res;
return 0;
}

Permutations

Solution:

Sử dụng hai vòng lặp while để xây dựng các mảng v1 và v2.

Mảng 1 (v1): các số lẻ không vượt quá n sau đó là các số chẵn không vượt quá n

Mảng 2 (v2: các số chẵn không vượt quá n sau đó là các số lẻ không vượt quá n

Khai báo hai biến kiểm tra check1 và check2 và gán giá trị ban đầu là true.

Sử dụng vòng lặp for để kiểm tra điều kiện sau đối với cả v1 và v2:

Kiểm tra xem khoảng cách giữa các số liên tiếp có bằng 1 hoặc có phần tử lớn hơn n. Nếu có, đặt
biến kiểm tra tương ứng (check1 hoặc check2) thành false và thoát khỏi vòng lặp.

Kiểm tra giá trị của biến kiểm tra check1 và check2:

Nếu check1 là true, in ra mảng v1.

Nếu check2 là true, in ra mảng v2.

Nếu cả hai biến kiểm tra là false, in ra "NO SOLUTION".

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n, a1 = 1, b1 = 2, a2 = 2, b2 = 1; cin >> n;
vector<int> v1, v2;
while (a1 <= n){
v1.push_back(a1), v2.push_back(a2);
a1 += 2, a2 += 2;
}
while (b1 <= n){
v1.push_back(b1), v2.push_back(b2);
b1 += 2, b2 += 2;
}
bool check1 = true, check2 = true;
for (int i = 0; i < v1.size() - 1; i++){
if (abs(v1[i] - v1[i + 1]) == 1 || v1[i] > n || v1[i + 1] > n){
check1 = false;
break;
}
}
for (int i = 0; i < v2.size() - 1; i++){
if (abs(v2[i] - v2[i + 1]) == 1 || v2[i] > n || v2[i + 1] > n){
check2 = false;
break;
}
}
if (check1) for (int i : v1) cout << i << " ";
else if (check2) for (int i : v2) cout << i << " ";
else cout << "NO SOLUTION";
return 0;
}

Number Spiral

Solution:

Nếu x >= y, ta có hai trường hợp:

Nếu x là số chẵn, tính kết quả theo công thức: (x - 1) * (x - 1) + (x - y) + x.

Nếu x là số lẻ, tính kết quả theo công thức: (x - 1) * (x - 1) + y.

Nếu y là số chẵn, tính kết quả theo công thức: (y - 1) * (y - 1) + x.

Nếu y là số lẻ, tính kết quả theo công thức: (y - 1) * (y - 1) + (y - x) + y.

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int t; cin >> t;
while (t--){
int x, y; cin >> x >> y;
if (x >= y){
if (x % 2 == 0) cout << (x - 1) * (x - 1) + (x - y) + x << "\
n";
else cout << (x - 1) * (x - 1) + y << "\n";
}
else if (y % 2 == 0) cout << (y - 1) * (y - 1) + x << "\n";
else cout << (y - 1) * (y - 1) + (y - x) + y << "\n";
}
return 0;
}

Two Knights
Solution:

Sử dụng vòng lặp for để duyệt từ i từ 1 đến n.

Tìm kết quả bằng công thức (i * i) * (i * i - 1) / 2 - 4 * (i - 2) * (i - 1).

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n; cin >> n;
for (int i = 1; i <= n; i++){
cout << ((i * i) * (i * i - 1) / 2) - 4 * (i - 2) * (i - 1) << "\
n";
}
return 0;
}

Two Sets
Solution:

Tính tổng s của các số từ 1 đến n bằng công thức s = n * (n + 1) / 2.

Nếu s không chia hết cho 2 không, in ra "NO”.

Nếu s chia hết cho 2, in ra “YES”, chia s cho 2 để có thành hai tập con có tổng bằng nhau.

Sử dụng một vector v để lưu trữ các số thuộc tập con thứ nhất và một map m để đánh dấu các số đã
được sử dụng.

Duyệt từ i từ n đến 1:

Nếu s lớn hơn i: trừ i ra khỏi s, đánh dấu i đã được sử dụng vào map và thêm i vào vector v.

In ra số lượng phần tử của tập con thứ nhất và các số trong tập con thứ nhất theo thứ tự ngược
lại(vector v).

In ra số lượng phần tử của tập con thứ hai (là n - v.size()) và các số trong tập con thứ hai theo thứ tự
từ 1 đến n nếu chưa được đánh dấu trong m.

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n; cin >> n;
int s = n * (n + 1) / 2;
if (s % 2){cout << "NO"; return 0;}
s /= 2;
cout << "YES\n";
vector<int> v;
unordered_map<int, int> m;
for (int i = n; i > 0; i--){
if (s < i) continue;
s -= i, m[i] = 1, v.push_back(i);
}
cout << v.size() << "\n";
for (int i = v.size() - 1; i >= 0; i--) cout << v[i] << " ";
cout << "\n" << n - v.size() << "\n";
for (int i = 1; i <= n; i++) if (!m[i]) cout << i << " ";
return 0;
}

Bit Strings

Solution:
Số cách để tạo ra mảng xâu đó là 2 ^ n, do đó kết quả sẽ là 2 ^ n % 1000000007.

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

#define M 1000000007

int power(int a, int b)


{
if (b == 0) return 1;
if (b == 1) return a % M;
int t = power(a, b / 2);
t = (t * t) % M;
if (b % 2 == 0) return t;
return ((a % M) * t) % M;
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n; cin >> n;
cout << power(2, n);
return 0;
}

Trailing Zeros

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n, res = 0; cin >> n;
for (int i = 5; i <= n; i *= 5) res += n / i;
cout << res;
return 0;
}

Coin Piles
Solution:
Tính giá trị t là sự chênh lệch giữa a và b.

Tạo ra hai biến A và B lần lượt bằng a và b, sau đó điều chỉnh dựa trên giá trị t:

Nếu a >= b, trừ A đi 2 lần t và trừ B đi t.

Ngược lại, trừ A đi t và trừ B đi 2 lần t.

Kiểm tra xem A và B có lớn hơn hoặc bằng 0 và có thỏa mãn điều kiện (a + b) % 3 == 0 không.

Nếu (a + b) % 3 == 0 có nghĩa là có thể làm trống cả hai đống.

In ra "YES", ngược lại in "NO”.

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n; cin >> n;
while (n--){
int a, b; cin >> a >> b;
int t = abs(a - b), A = a, B = b;
if (a >= b) A -= 2 * t, B -= t;
else A -= t, B -= 2 * t;
if (A >= 0 && B >= 0 && (a + b) % 3 == 0) cout << "YES\n";
else cout << "NO\n";
}
return 0;
}

Palindrome Reorder

Solution:
Dùng unordered_map m để đếm số lần xuất hiện của các ký tự trong xâu s.
Nếu số lần xuất hiện của các ký tự trong xâu s là chẵn thì lưu vào mảng v1, không thì lưu vào mảng
v2.
Sử dụng 2 con trỏ l và r. Tiến hành sắp các phần tử của mảng v1 vào 2 đầu của xâu
Sau đó sắp xếp các phần tử của mảng v2 vào khoảng còn lại ở giữa xâu
Kiểm tra lại, nếu s không là xâu palindrome thì in ra “NO”.

Code:
#include "bits/stdc++.h"
using namespace std;
#define int long long

#define fi first
#define se second

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
string s; cin >> s;
unordered_map<int, int> m;
for (char i : s) m[i]++;
vector<char> v1, v2;
for (auto i : m){
if (i.se % 2 == 0){
for (int j = 0; j < i.se; j++) v1.push_back(i.fi);
}
else for (int j = 0; j < i.se; j++) v2.push_back(i.fi);
}
sort(v1.rbegin(), v1.rend()), sort(v2.rbegin(), v2.rend());
int l = 0, r = s.size() - 1;
while (l < r && !v1.empty()){
s[l] = v1.back(), v1.pop_back();
if (v1.empty()) break;
s[r] = v1.back(), v1.pop_back();
l++, r--;
}
while (l <= r && !v2.empty()){
s[l] = v2.back(), v2.pop_back();
if (v2.empty()) break;
s[r] = v2.back(), v2.pop_back();
l++, r--;
}
for (int i = 0; i < s.size(); i++){
if (s[i] != s[s.size() - i - 1]){cout << "NO SOLUTION"; return 0;}
}
cout << s;
return 0;
}

Gray Code

Solution:

Sử dụng đệ quy để lấy các xâu nhị phân có độ dài n – 1, thêm 0 và 1 vào

Code:

#include "bits/stdc++.h"
using namespace std;

#define int long long

vector<string> graycode(int n)
{
if (n <= 0) return {"0"};
if (n == 1) return {"0", "1"};
vector<string> t = graycode(n - 1);
vector<string> v;
for (int i = 0; i < t.size(); i++){
string s = t[i];
v.push_back("0" + s);
}
for (int i = t.size() - 1; i >= 0; i--){
string s = t[i];
v.push_back("1" + s);
}
return v;
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n; cin >> n;
vector<string> v = graycode(n);
for (auto i : v) cout << i << "\n";
return 0;
}

Tower of Hanoi

Solution:

In ra tổng số bước tối thiểu cần thiết để chuyển n đĩa từ cọc A sang cọc C là 2^n – 1

Gọi hàm đệ quy thnđể thực hiện chuyển thứ n đĩa từ cọc A sang cọc C, sử dụng cọc trung gian B.
Hàm này sẽ thực hiện theo quy tắc:

Chuyển đĩa n-1 từ cọc A sang cọc B, sử dụng cọc C làm cọc trung gian.

In ra bước chuyển đĩa từ cọc A sang cọc C.

Chuyển đĩa n-1 từ cọc B sang cọc C, sử dụng cọc A làm cọc trung gian.

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

void thn(int n, int a, int b, int c)


{
if (n == 0) return;
thn(n - 1, a, c, b);
cout << a << " " << c << "\n";
thn(n - 1, b, a, c);
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n; cin >> n;
cout << pow(2, n) - 1 << "\n";
thn(n, 1, 2, 3);
return 0;
}

Creating Strings

Solution:
Sử dụng thuật toán quay lui để tạo nên tất cả hoán vị của xâu s;
Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

string s;
int f[10] = {};
vector<string> res;
unordered_map<string, int> m;
void ql(string t)
{
if (!m[t]++ && t.size() == s.size()) res.push_back(t);
else if (t.size() < s.size()){
for (int i = 0; i < s.size(); i++){
if (!f[i]){
f[i] = 1;
ql(t + s[i]);
f[i] = 0;
}
}
}
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin >> s;
sort(s.begin(), s.end());
ql("");
cout << res.size() << "\n";
for (auto i : res) cout << i << "\n";
return 0;
}

Apple Division

Solution:
Sử dụng thuật toán quay lui để tìm độ chênh lệch nhỏ nhất sau khi chia mảng a thành 2 phần.

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

int n, res = INT_MAX;


int a[25];
void ql(int i, int s1, int s2)
{
if (i == n) res = min(abs(s1 - s2), res);
if (i < n){
ql(i + 1, s1 + a[i], s2);
ql(i + 1, s1, s2 + a[i]);
}
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin >> n;
for (int i = 0; i < n; i++) cin >> a[i];
ql(0, 0, 0);
cout << res;
return 0;
}

Chessboard and Queens

Solution:
Hàm check() để kiểm tra xem quân hậu ở vị trí I, j có bị con hậu khác ăn mất không.
Nếu không thì Đặt quân hâu mới tại vị trí i, j, tiếp tục quay lui để tìm cách đặt them quân hậu khác

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

int res = 0;
char a[8][8];
bool check(int row, int col)
{
for (int i = 0; i < 8; i++) {
if (a[i][col] == 'Q' || a[row][i] == 'Q') return false;
}
for (int i = row, j = col; i >= 0 && j >= 0; i--, j--) {
if (a[i][j] == 'Q') return false;
}
for (int i = row, j = col; i >= 0 && j < 8; i--, j++) {
if (a[i][j] == 'Q') return false;
}
return true;
}
void ql(int i)
{
if (i == 8) res++;
else if (i < 8){
for (int j = 0; j < 8; j++){
if (a[i][j] == '.' && check(i, j)){
a[i][j] = 'Q';
ql(i + 1);
a[i][j] = '.';
}
}
}
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
for (int i = 0; i < 8; i++) for (int j = 0; j < 8; j++){
cin >> a[i][j];
}
ql(0);
cout << res;
return 0;
}

Digit Queries
Solution:

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

int power(int a, int b)


{
if (b == 0) return 1;
if (b == 1) return a;
int t = power(a, b / 2);
t = t * t;
if (b % 2 == 0) return t;
return a * t;
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int t; cin >> t;
while (t--){
int n; cin >> n;
int c = 1;
for (int p = 9;; n -= p, c++, p = 9 * power(10, c - 1) * c){
if (n - p <= 0) break;
}
n--;
int x = n / c, y = n % c;
int res = power(10, c - 1) + x;
string s = to_string(res);
cout << s[y] << "\n";
}
return 0;
}

Grid Paths

Solution:
Sử dụng đệ quy để di chuyển từ ô 1, 1 đến n, 1.
Sử dùng nhánh cận để tối ưu.
Nếu tới ô vuông n, 1 trước khi tới các ô khác thì sẽ kết thúc đệ quy
Nếu không để đi tới và có thể rẽ trái hoặc phải, lưới sẽ chia thành hai phần chứa các ô vuông
chưa được thăm thì cũng không thể truy cập tất cả các ô vuông nữa nên sẽ kết thúc đệ quy.

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

string s;
int d = 0;
int a[10][10] = {};
void dq(int i, int j, int k)
{
if (a[i][j]) return;
if (i > 7 || j > 7 || i == 0 || j == 0) return;
if ((!a[i][j - 1] && !a[i][j + 1]) && (a[i - 1][j] && a[i + 1][j]))
return;
if ((a[i][j - 1] && a[i][j + 1]) && (!a[i - 1][j] && !a[i + 1][j]))
return;
if (i == 7 && j == 1){
if (k == 48) d++;
return;
}
if (k == 48) return;
a[i][j] = 1;
if (s[k] == '?'){
if (!a[i + 1][j]) dq(i + 1, j, k + 1);
if (!a[i - 1][j]) dq(i - 1, j, k + 1);
if (!a[i][j - 1]) dq(i, j - 1, k + 1);
if (!a[i][j + 1]) dq(i, j + 1, k + 1);
}
else if (s[k] == 'D') dq(i + 1, j, k + 1);
else if (s[k] == 'U') dq(i - 1, j, k + 1);
else if (s[k] == 'L') dq(i, j - 1, k + 1);
else dq(i, j + 1, k + 1);
a[i][j] = 0;
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin >> s;
for (int i = 0; i <= 7; i++){
a[0][i] = 1, a[8][i] = 1, a[i][0] = 1, a[i][8] = 1;
}
a[8][8] = 1;
dq(1, 1, 0);
cout << d;
return 0;
}

Sorting and Searching

Distinct Numbers

Solution:
Thêm các phần tử của mảng a vào mảng set. Vì set không lưu trữ 2 phần tử giống nhau nên độ dài
của set là kết quả.
Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n; cin >> n;
set<int> s;
for (int i = 0, a; i < n; i++) cin >> a, s.insert(a);
cout << s.size();
return 0;
}
Apartments

Solution:
Sắp xếp các mảng a, b theo thứ tự tăng dần.
Sử dùng 2 con trỏ với l = 0 và r = 0
Nếu b[r] nằm trong khoảng kích thước mong muốn a[l] thì kết quả tăng thêm 1 và tăng l và r thêm 1
Nếu b[r] quá lớn so với kích thước mong muốn a[l] thì tăng l thêm 1
Ngược lại tăng r thêm 1
Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n, m, k; cin >> n >> m >> k;
int a[n], b[m];
for (int &i : a) cin >> i;
for (int &i : b) cin >> i;
sort(a, a + n), sort(b, b + m);
int res = 0, l = 0, r = 0;
while (l < n && r < m){
if (a[l] <= b[r] + k && a[l] >= b[r] - k) res++, l++, r++;
else if (b[r] - k > a[l]) l++;
else if (a[l] > b[r] + k) r++;
}
cout << res;
return 0;
}

Ferris Wheel

Solution:
Sắp xếp dãy a theo thứ tự tăng dần.
Tạo biến kết quả gồm n chiếc thuyền.
Sử dụng 2 con trỏ với l = 0, và r = n – 1;
Nếu trọng lượng của 2 đứa trẻ a[l] và a[r] không vượt quá x thì giảm kết quả đi 1, tăng l và r thêm 1
Ngược lại thì giảm r đi 1.
Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n, x; cin >> n >> x;
int a[n];
for (int &i : a) cin >> i;
sort(a, a + n);
int l = 0, r = n - 1, res = n;
while (l < r){
if (a[l] + a[r] <= x) res--, l++, r--;
else r--;
}
cout << res;
return 0;
}

Concert Tickets

Solution:
Tạo 1 mảng multiset để lưu trữ các phần tử trong mảng a
Đối với giá tối đa cho mỗi khách hàng thì sử dụng lower_bound để tìm giá vé lớn nhất nhỏ hơn bằng
giá tối đa của mỗi khách hang.
Nếu không tìm thấy thì in -1
Ngược lại thì xóa phần tử tìm được đi
Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n, m; cin >> n >> m;
multiset<int, greater<int>> a;
int b[m];
for (int i = 0, t; i < n; i++) cin >> t, a.insert(t);
for (int i = 0, b; i < m; i++){
cin >> b;
auto it = a.lower_bound(b);
if (it == a.end()) cout << "-1\n";
else{
cout << *it << "\n";
a.erase(it);
}
}
return 0;
}

Restaurant Customers

Solution:
Sắp xếp các phần tử của mảng a theo thời gian đến tăng dần
Tạo mảng multiset để lưu các thời gian về, thêm 0 vào mảng.
Duyệt qua các phần tử mảng a
Nếu thời gian về của khách hàng thứ I lớn hơn phần tử nhỏ nhất của mảng multiset thì xóa phần tử
đó trong multiset đi
Ngược lại thì tăng kết quả thêm 1.
Thêm thời gian về của khách hàng I vào multiset

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

#define fi first
#define se second

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n; cin >> n;
vector<pair<int, int>> a(n);
for (int i = 0; i < n; i++) cin >> a[i].fi >> a[i].se;
sort(a.begin(), a.end(), [](pair<int, int> a, pair<int, int> b){
if (a.fi == b.fi) return a.se < b.se;
return a.fi < b.fi;
});
int res = 1;
multiset<int> s = {0};
for (int i = 0; i < n; i++){
if (a[i].fi > *s.begin()) s.erase(s.begin());
else res++;
s.insert(a[i].se);
}
cout << res;
return 0;
}

Movie Festival

Solution:
Sắp xếp mảng a theo thời gian hết phim tăng dần
Tạo m = 0 là thời gian hết phim ban đầu;
Duyệt qua mảng a
Nếu thời gian bắt đầu phim thứ I lớn hơn m
Gán m = a[i], tăng kết quả thêm 1
Code:
#include "bits/stdc++.h"
using namespace std;

#define fi first
#define se second

int main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n; cin >> n;
pair<int, int> a[n];
for (int i = 0; i < n; i++) cin >> a[i].fi >> a[i].se;
sort(a, a + n, [](pair<int, int> a, pair<int, int> b){
return a.se < b.se;
});
int res = 0, m = 0;
for (int i = 0; i < n; i++){
if (a[i].fi >= m) m = a[i].se, res++;
}
cout << res;
return 0;
}

Sum of Two Values

Solution:
Tạo map m1 để lưu trữ vị trí của các phần tử trong mảng, m2 để lưu trữ số lần xuất hiện của các
phần tử trong mảng
Duyệt qua mảng
Nếu số lần xuất hiện của phần tử đó trong mảng là nhỏ hơn hoặc bằng 1, bạn đặt giá trị của m1[a[i]]
bằng 0. Để không xem xét lại phần tử này trong việc tìm kiếm sau.

Kiểm tra xem có một phần tử có giá trị x - a[i] trong m1 không. Nếu có, nghĩa là đã tìm thấy một cặp
phần tử có tổng bằng x. In ra vị trí của cặp phần tử này.

Gán lại vị trí của a[i] trong m1.

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n, x; cin >> n >> x;
int a[n];
map<int, int> m1, m2;
for (int i = 1; i <= n; i++) cin >> a[i], m1[a[i]] = i, m2[a[i]]++;
for (int i = 1; i <= n; i++){
if (m2[a[i]] <= 1) m1[a[i]] = 0;
if (m1[max(x - a[i], (int) 0)]){
cout << i << " " << m1[x - a[i]] << "\n";
return 0;
}
m1[a[i]] = i;
}
cout << "IMPOSSIBLE";
return 0;
}

Maximum Subarray Sum


Solution:
Sử dụng thuật toán kanade để tìm dãy con có tổng lớn nhất
Code:
#include "bits/stdc++.h"

using namespace std;

#define int long long

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n; cin >> n;
int a[n];
for (int i = 0; i < n; i++) cin >> a[i];
int s1 = 0, s2 = INT_MIN;
for (int i = 0; i < n; i++){
s1 += a[i], s2 = max(s1, s2);
if (s1 <= 0) s1 = 0;
}
cout << s2;
return 0;
}

Stick Lengths

Solution:
Sắp xếp mảng a tăng dần.
Chia thành 2 trường hợp:
Nếu n là số chẵn thì tổng chi phí tối thiểu bằng khoảng cách của a[i] với phần tử ở giữa
Ngược lại
Tổng chi phí tối thiểu bằng min của khoảng cách của a[i] với phần tử ở giữa a[n / 2] và khoảng cách
của a[i] với phần tử ở giữa a[n / 2 + 1]
Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n, res = LLONG_MAX; cin >> n;
int a[n];
for (int &i : a) cin >> i;
sort(a, a + n);
if (n % 2){
int m = a[n / 2], s = 0;
for (int i = 0; i < n; i++) s += abs(a[i] - m);
res = min(s, res);
}
else{
int m1 = a[n / 2 - 1], m2 = a[n / 2];
int s1 = 0, s2 = 0;
for (int i = 0; i < n; i++) s1 += abs(a[i] - m1), s2 += abs(a[i] -
m2);
res = min({s1, s2, res});
}
cout << res;
return 0;
}

Missing Coin Sum


Solution:
Duyệt qua mảng
TÍnh tổng cá giá trị a[i]
Nếu a[i] > tổng đó thì đã bị thiếu tổng đó và in ra kết quả là tổng đó
Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n; cin >> n;
int a[n];
for (int &i : a) cin >> i;
sort(a, a + n);
int res = 1;
for(int i = 0; i < n && a[i] <= res; i++) res += a[i];
cout << res;
return 0;
}

Collecting Numbers

Solution:
Lưu các vị trí của các phần tử của mảng vào map
Tạo k là các số cần thu thập, ban đầu k = 1
Dùng vòng lặp while với đk k <= n
Nếu vị trí của k trong mảng bé hơn vị trí của k + 1, thì có nghĩa là trong 1 lần đi có thể thu thập của 2
số, tăng k lên 1
Nếu không thì tăng kết quả lên 1 và k lên 1.
Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n; cin >> n;
int a[n];
map<int, int> m;
for (int i = 0; i < n; i++) cin >> a[i], m[a[i]] = i;
int res = 0, k = 1;
while (k <= n){
if (m[k] < m[k + 1]) k++;
else res++, k++;
}
cout << res;
return 0;
}

Playlist
Solution:
Duyệt qua mảng a với x = 0
Gán x = vị trí xuất hiện cuối cùng của a[i] trước i
Kết quả là đọp dài lớn nhất của I và x
Gán vị trí xuất hiện cuối cùng của a[i] là i
Code:
#include "bits/stdc++.h"
using namespace std;

int main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n, res = 0; cin >> n;
int a[n];
for (int &i : a) cin >> i;
map<int, int> m;
for (int i = 0, x = 0; i < n; i++){
x = max(m[a[i]], x);
res = max(i - x + 1, res);
m[a[i]] = i + 1;
}
cout << res;
return 0;
}

Towers

Solution:
Tạo mảng multiset
Duyệt qua mảng a
Nếu chưa có tháp nào thì mảng multiset thêm phần tử a[i]
Ngược lại
Dùng upper_bound để kt xem có tháp nào có tầng thêm cùng lớn hơn a[i] ko
Nếu có thì xóa phần tử đó đi
Thêm a[i] vào multiset
Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n; cin >> n;
int a[n];
for (int &i : a) cin >> i;
multiset<int> s;
for (int i = 0; i < n; i++){
if (i == 0) s.insert(a[i]);
else{
auto it = s.upper_bound(a[i]);
if (it != s.end()) s.erase(it);
s.insert(a[i]);
}
}
cout << s.size();
return 0;
}
Traffic Lights
Solution:
Tạo 1 set với 2 cột ban đầu ở vị trí 0 và x, 1 multiset với khoảng cách ngắn nhất ban đầu là x
Duyệt qua mảng a
Sử dụng upper_bound để tìm cột đèn đứng sau a[i], đặt nó là it1
It2 là cột đèn đứng trước it1, a[i]
Xóa khoảng cách của 2 cột trong multiset đi
Thêm khoảng cách của cột đèn 1 với a[i], và cột đèn 2 với a[i] vào multiset
Kết quả là phần tử nhỏ nhất trong multiset

Code:
#include "bits/stdc++.h"
using namespace std;
#define int long long

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int x, n; cin >> x >> n;
int a[n];
for (int &i : a) cin >> i;
set<int> s = {0, x};
multiset<int> ms = {x};
for (int i = 0; i < n; i++){
auto it1 = s.upper_bound(a[i]);
auto it2 = it1; it2--;
ms.erase(ms.find(*it1 - *it2));
ms.insert(*it1 - a[i]), ms.insert(a[i] - *it2);
cout << *ms.rbegin() << " ";
s.insert(a[i]);
}
return 0;
}
Josephus Problem I
Solution:

Cho mảng a là các số từ 1 đến n

Duyệt qua mảng a

Chọn phần tử a, dùng map để đánh dấu phần tử này đã được chọn

Dùng thêm 1 vòng lặp nữa để bỏ qua 1 số rồi mới chọn tiếp
Code:
#include "bits/stdc++.h"

using namespace std;

#define int long long

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n; cin >> n;
int a[n];
for (int i = 0; i < n; i++) a[i] = i + 1;
int i = 0;
unordered_map<int, int> m;
vector<int> res;
while (res.size() < n){
int d = 0;
for (int j = i;; j++){
if (!m[a[j % n]]) d++;
if (d == 2){i = j; break;}
}
m[a[i % n]]++, res.push_back(a[i % n]);
}
for (int i : res) cout << i << " ";
return 0;
}

Josephus Problem II
Solution:
Sử dùng ordered_set để tối ưu hóa các thao tác
In ra phần tử thứ I trong set, xóa phần tử đã in đi sau đó tăng I thêm k.
Code:
#include "bits/stdc++.h"
#include "ext/pb_ds/assoc_container.hpp"
#include "ext/pb_ds/tree_policy.hpp"
using namespace std;
using namespace __gnu_pbds;

#define int long long

template<typename T> using ordered_set = tree<T, null_type, less<T>,


rb_tree_tag, tree_order_statistics_node_update>;
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n, k; cin >> n >> k;
ordered_set<int> s;
for (int i = 1; i <= n; i++) s.insert(i);
int i = k % n;
while (!s.empty()){
if (s.size() == 0) break;
auto it = s.find_by_order(i % s.size());
cout << *it << " ";
s.erase(it);
if (s.size() == 0) break;
(i += (k % s.size())) %= s.size();
}
return 0;
}

Room Allocation

Solution:
Sắp xếp dãy a theo thời gian đến tăng dần
Tạo 1 multiset ban đầu là {0, 1}
Duyệt qua mảng a
Nếu thời gian bắt đầu của a[i] lớn hơn thời gian kết thúc trong multiset
Thì thay thế phần tử đó trong multiset thành thời gian kết thúc của a[i]
Khi đó thì căn phòng của người I vẫn là phòng trong multiset
Ngược lại
Thì thêm tg kết thúc của a[i] và số phòng trong multiset cộng thêm 1
Khi đó thì căn phòng của người I là phòng trong multiset cộng 1
Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

#define fi first
#define se second

struct customer{
int st, end, id;
};
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n; cin >> n;
customer a[n];
for (int i = 0; i < n; i++) cin >> a[i].st >> a[i].end, a[i].id = i;
sort(a, a + n, [](customer a, customer b){
return (a.st == b.st) ? a.end < b.end : a.st < b.st;
});
int room = 1;
multiset<pair<int, int>> s = {{0, 1}};
vector<int> r(n);
for (int i = 0; i < n; i++){
pair<int, int> t = *s.begin();
if (t.fi < a[i].st){
s.erase(s.begin());
s.insert({a[i].end, t.se});
r[a[i].id] = t.se;
}
else{
s.insert({a[i].end, ++room});
r[a[i].id] = room;
}
}
cout << room << "\n";
for (int i = 0; i < n; i++) cout << r[i] << " ";
return 0;
}

Factory Machines

Solution:
Sử dụng tìm kiếm nhị phân cho từng thời gian để máy sản xuất t sản phẩm
Đối với từng cái, sử dụng vòng lặp for để kt xem với thời gian t thì các máy có thể sản xuất được t
sản phẩm hay ko, nếu có kết quả là thời gian ít nhất.
Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n, t; cin >> n >> t;
int a[n];
for (int i = 0; i < n; i++) cin >> a[i];
int l = 0, r = 1e18, res = 0;
while (l <= r){
int m = (l + r) / 2, s = 0;
for (int i : a){
s += m / i;
if (s >= t) break;
}
if (s >= t) res = m, r = m - 1;
else l = m + 1;
}
cout << res;
return 0;
}
Tasks and Deadlines
Solution:
Sắp xếp dãy a theo thời hạn thực hiện nhiệm vụ
Tạo 1 biến t = 0, t là thời gian thực hiện các nhiệm vụ
Duyệt qua mảng a
Cập nhập giá trị t theo các thời gian thực hiện nhiệm vụ thứ i
Kêt quả sẽ là thời hạn thực hiện nhiêm vụ đó thừ đi thời gian đã làm các nhiệm vụ trước(t)
Code:
#include "bits/stdc++.h"

using namespace std;

#define int long long

#define fi first
#define se second

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n; cin >> n;
pair<int, int> a[n];
for (auto &i : a) cin >> i.fi >> i.se;
sort(a, a + n, [](pair<int, int> a, pair<int, int> b){
return ((a.fi == b.fi) ? a.se < b.se : a.fi < b.fi);
});
int t = 0, res = 0;
for (int i = 0; i < n; i++){
t += a[i].fi;
res += a[i].se - t;
}
cout << res;
return 0;
}

Reading Books
Solution:
Có 2 cách để đọc hết những quyển sách
Một là Kotivalo đọc sách theo thời gian giảm dần, Justiina tăng dần, khi đó thời
gian cần để đọc hết là tổng của mảng a
Hai là một người đọc quyển sách có tg nhiều nhất, còn người kia đọc các quyển
còn lại
Kết quả là min của hai cách
Code:
#include "bits/stdc++.h"

using namespace std;

#define int long long

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n, s = 0; cin >> n;
int a[n];
for (int &i : a) cin >> i, s += i;
int m = *max_element(a, a + n);
cout << max(s, m * 2);
return 0;
}

Sum of Three Values


Solution:
Sử dụng 2 vòng lặp để tìm tổng của 2 phần tử, còn phần tử còn lại dùng tìm kiếm nhị phân. Nếu tìm
được 3 phần từ thõa mản yêu cầu thì in ra vị trí các phần tử đó.
Code:
#include "bits/stdc++.h"

using namespace std;

#define int long long

#define fi first
#define se second

int binarysearch(pair<int, int> a[], int l, int r, int x)


{
while (l <= r){
int m = (l + r) / 2;
if (a[m].fi == x) return a[m].se;
if (a[m].fi < x) l = m + 1;
else r = m - 1;
}
return -1;
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n, x; cin >> n >> x;
pair<int, int> a[n];
for (int i = 0; i < n; i++) cin >> a[i].fi, a[i].se = i;
sort(a, a + n);
for (int i = 0; i < n - 2; i++){
for (int j = i + 1; j < n - 1; j++){
int t = binarysearch(a, j + 1, n - 1, x - a[i].fi - a[j].fi);
if (t != -1){
cout << a[i].se + 1 << " " << a[j].se + 1 << " " << t + 1;
return 0;
}
}
}
cout << "IMPOSSIBLE";
return 0;
}

Sum of Four Values

Solution:
Đầu tiên, dùng 2 for để gộp giá trị của tổng 2 phần tử của mảng, lưu vào map
Sau đó lại dùng 2 for để tìm giá trị của tổng 2 phần tử trong mảng.
Đối với từng giá trị kt xem trong map có tổng nào mà cộng với tổng này bằng x hay ko. Nếu có thì in
ra kq.
Code:
#include "bits/stdc++.h"

using namespace std;

#define int long long

#define fi first

#define se second

signed main()

ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

int n, x; cin >> n >> x;

pair<int, int> a[n];

for (int i = 0; i < n; i++) cin >> a[i].fi, a[i].se = i;

unordered_map<int, pair<int, int>> m;

for (int i = 0; i < n - 1; i++){

for (int j = i + 1; j < n; j++) m[a[i].fi + a[j].fi] = {i, j};

for (int i = 0; i < n - 1; i++){

for (int j = i + 1; j < n; j++){

int s = a[i].fi + a[j].fi;

if (m.find(x - s) != m.end()){

pair<int, int> t = m[x - s];

if (t.fi != a[i].se && t.fi != a[j].se && t.se != a[i].se && t.se != a[j].se){

cout << a[i].se + 1 << " " << a[j].se + 1 << " " << t.fi + 1 << " " << t.se + 1;

return 0;

}
}

cout << "IMPOSSIBLE";

return 0;

Nearest Smaller Values

Solution:
Tạo 1 vector v để lưu trữ các giá trị của mảng a
Duyệt qua mảng a
Chạy vòng lặp while đến khi phần tử cuối của vector nhỏ hơn a[i], nếu nó lớn hơn hoặc bằng a[i] thì
xóa đi
Nếu sau khi chạy, vector rỗng thì có nghĩa là không có phần tử nào nhỏ hơn
Ko thì in ra phần tử cuối của vector v
Thêm a[i] vào vector v

Code:
#include "bits/stdc++.h"
using namespace std;

#define fi first
#define se second

int main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n; cin >> n;
pair<int, int> a[n];
for (int i = 0; i < n; i++) cin >> a[i].fi, a[i].se = i;
vector<pair<int, int>> v;
for (int i = 0; i < n; i++){
while (!v.empty() && v.back().fi >= a[i].fi) v.pop_back();
if (v.empty()) cout << "0 ";
else cout << v.back().se + 1 << " ";
v.push_back({a[i].fi, a[i].se});
}
return 0;
}

Subarray Sums I

Solution:
Tạo 1 tổng s = 0, 1 map để lưu trữ các tổng của dãy a
Duyệt qua mảng a
Cập nhật giá trị của a[i] vào s
Nếu tổng s = x thì tăng kết quả thêm 1
Nếu tìm được phần tử bằng s – x thì kết quả tăng thêm số lượng phần tử bằng s –x
Dùng map để lưu trữ số lượng tổng s
Code:
#include "bits/stdc++.h"

using namespace std;

#define int long long

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n, x; cin >> n >> x;
int a[n];
for (int &i : a) cin >> i;
int res = 0, s = 0;
unordered_map<int, int> m;
for (int i = 0; i < n; i++){
s += a[i];
if (s == x) res++;
if (m.count(s - x)) res += m[s - x];
m[s]++;
}
cout << res;
return 0;
}

Subarray Sums II
Solution:
Tạo 1 tổng s = 0, 1 map để lưu trữ các tổng của dãy a
Duyệt qua mảng a
Cập nhật giá trị của a[i] vào s
Nếu tổng s = x thì tăng kết quả thêm 1
Nếu tìm được phần tử bằng s – x thì kết quả tăng thêm số lượng phần tử bằng s –x
Dùng map để lưu trữ số lượng tổng s

Code:
#include "bits/stdc++.h"

using namespace std;

#define int long long

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n, x; cin >> n >> x;
int a[n];
for (int &i : a) cin >> i;
int res = 0, s = 0;
unordered_map<int, int> m;
for (int i = 0; i < n; i++){
s += a[i];
if (s == x) res++;
if (m.count(s - x)) res += m[s - x];
m[s]++;
}
cout << res;
return 0;
}

Subarray Divisibility
Solution:
Tạo 1 mảng mod và tống s = 0
Duyệt qua mảng a
Đếm số lượng số dư khi n chia cho tống s, khi s công thêm a[i]
Duyệt từ 0 đến n – 1
Sử dụng công thức để tính ra kết quả
Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n; cin >> n;
int a[n];
for (int &i : a) cin >> i;
int mod[n] = {};
int res = 0, s = 0;
for (int i = 0; i < n; i++) s += a[i], mod[((s % n) + n) % n]++;
for (int i = 0; i < n; i++) res += mod[i] * (mod[i] - 1) / 2;
res += mod[0];
cout << res;
return 0;
}

Subarray Distinct Values


Solution:
Dùng map để lưu trữ số lượng các phần tử để tính toán
Dùng 2 con trỏ l = 0, r = 0
Tăng số lượng phần tử a[r] lên 1
Sử dùng vòng lặp while với đk là số lượng các phần tử là bé hơn hoặc bằng k thì dừng lại
Trong đó, sẽ giảm số lượng phần tử a[l] đi, tăng l thêm 1
Kết quả sẽ cộng thêm khoảng cách của r và l, tăng r thêm 1
Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n, k; cin >> n >> k;
int a[n];
for (int i = 0; i < n; i++) cin >> a[i];
int l = 0, r = 0, res = 0;
unordered_map<int, int> m;
while (r < n){
m[a[r]]++;
while (m.size() > k){
m[a[l]]--;
if (m[a[l]] == 0) m.erase(a[l]);
l++;
}
res += r - l + 1, r++;
}
cout << res;
return 0;
}

Array Division
Solution:
Sử dùng tìm kiếm nhị phân, với m là tổng lớn nhất khi chia mảng a thành các mảng con
Tạo tổng các phần tử ban đầu là 0, số lượng mảng con là 1
Sử dùng vòng lặp qua mảng a để kiểm tra
Nếu m bé hơn a[i] thì cập nhật số lượng mảng lên lớn nhất vì không có mảng nào phù hợp, dừng
kiểm tra
Nếu tổng trên cộng a[i] lớn hơn m thì tăng số lượng mảng con lên 1, tổng trên trả lại về 0
Cập nhật giá trị của tổng

Nếu số sô lượng mảng con > k thì tăng l để tìm kiếm nhị phân các số lớn hơn
Ngược lại thì giảm r xuống giảm xuống, cập nhật kết quả
#include "bits/stdc++.h"
using namespace std;

#define int long long

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n, k; cin >> n >> k;
int a[n];
for (int &i : a) cin >> i;
int l = 1, r = 1e18, res = 1e18;
while (l <= r){
int m = (l + r) / 2;
int cnt = 1, s = 0;
for (int i = 0; i < n; i++){
if (a[i] > m) {cnt = INT_MAX; break;}
if (s + a[i] > m) cnt++, s = 0;
s += a[i];
}
if (cnt > k) l = m + 1;
else r = m - 1, res = min(m, res);
}
cout << res;
return 0;
}

Sliding Median

Solution:

Sử dụng ordered_set để tối ưu hóa các thao tác

Code:
#include "bits/stdc++.h"
using namespace std;
#include "ext/pb_ds/assoc_container.hpp"
#include "ext/pb_ds/tree_policy.hpp"
using namespace __gnu_pbds;

#define int long long

using ordered_set = tree<pair<int, int>, null_type, less<pair<int, int>>,


rb_tree_tag, tree_order_statistics_node_update>;

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n, k; cin >> n >> k;
int a[n];
for (int &i : a) cin >> i;
int t = 0;
ordered_set s;
for (int i = 0; i < k; i++) s.insert({a[i], t++});
cout << (*s.find_by_order((k - 1) / 2)).first << " ";
for (int i = 1; i + k - 1 < n; i++){
auto it = s.lower_bound({a[i - 1], 0});
s.erase(it), s.insert({a[i + k - 1], t++});
cout << (*s.find_by_order((k - 1) / 2)).first << " ";
}
return 0;
}

Sliding Cost

Solution:
Sử dụng ordered_set để tối ưu hóa các thao tác
Đầu tiên, tìm trung vị và tổng của k phần tử đầu tiên và lưu k phần tử đầu tiên vào ordered_set
Gọi d là tìm khoảng cách của trung vị và tổng k phần tử đầu tiên
Sau đó, duyệt qua mảng từ phần tử thứ 2 đên phần tử thứ n – k + 1.
Xóa đi phần tử thứ I – 1, và thêm phần tử thứ I + k - 1 vào ordered_set để tìm trung vị mới sau đó
xóa phần tử thứ i + k – 1 đi để tính toán, d giảm đi khoảng cách của phần tử thứ I – 1 và trung vị cũ
sau đó cập nhật d theo trung vị mới theo 3 điều kiện sau:
Nếu trung vị cũ bằng với trung vị mới, tức là trung vị ko thay đổi thì d cộng thêm khoảng cách của
trung vị mới và phần tử thứ I + k – 1
Nếu trung vị cũ bé hơn trung vị mới thì d sẽ cộng thêm số phần tử mà bé hơn trung vị mới nhân cho
khoảng cách của trung vị cũ và mới và trừ đi số phần tử mà lớn hơn hoặc bằng trung vị mới trong
mảng ordered_set
Còn nếu trung vị cũ lớn hơn trung vị mới thì d sẽ trừ đi số phần tử mà bé hơn trung vị mới nhân cho
khoảng cách của trung vị cũ và mới và cộng thêm số phần tử mà lớn hơn hoặc bằng trung vị mới
trong mảng ordered_set
Sau đó in ra kết quả là d
Cập nhật lại giá trung vị cũ và thêm phần tử thứ i + k – 1 vào ordered_set.
Code:
#include "bits/stdc++.h"
#include "ext/pb_ds/assoc_container.hpp"
#include "ext/pb_ds/tree_policy.hpp"
using namespace std;
using namespace __gnu_pbds;

#define int long long


#define fi first
#define se second

template<typename T>
using ordered_set = tree<T, null_type, less<T>, rb_tree_tag,
tree_order_statistics_node_update>;

int t = 0;
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n, k; cin >> n >> k;
int a[n];
for (int i = 0; i < n; i++) cin >> a[i];
ordered_set<pair<int, int>> s;
for (int i = 0; i < k; i++) s.insert({a[i], t++});
int oldm = (*s.find_by_order((k + 1) / 2 - 1)).fi, d = 0;
for (int i = 0; i < k; i++) d += abs(a[i] - oldm);
cout << d << " ";
for (int i = 1; i < n - k + 1; i++){
auto it = s.lower_bound({a[i - 1], 0});
s.erase(it);
s.insert({a[i + k - 1], t++});
int m = (*s.find_by_order((k + 1) / 2 - 1)).fi;
it = s.lower_bound({a[i + k - 1], 0});
s.erase(it);
d -= abs(a[i - 1] - oldm);
if (m == oldm) d += abs(a[i + k - 1] - m);
else if (m > oldm){
int l = s.order_of_key({m, 0});
int r = s.size() - l;
d += ((l * (m - oldm)) - (r * (m - oldm))) + abs(a[i + k - 1] -
m);
}
else if (m < oldm){
int l = s.order_of_key({m, 0}) + ((*s.lower_bound({m, 0})).fi
== m);
int r = s.size() - l;
d += -(l * (oldm - m)) + (r * (oldm - m)) + abs(a[i + k - 1] -
m);
}
s.insert({a[i + k - 1], 0});
oldm = m;
cout << d<< " ";
}
return 0;
}

Movie Festival II
Solution:

Sắp xếp dãy a theo theo thời gian bắt đầu phim tăng dần

Sử dụng multiset và thêm k phần tử 0 cho k người khi chưa xem phim gì thời gian kết thúc
của nó là 0

Dùng upper_bound để tìm bộ phim có thời gian kết thúc gần với a[i] nhất, nếu tìm được thì
tăng kết quả them 1

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

#define fi first
#define se second

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n, k; cin >> n >> k;
pair<int, int> a[n];
for (int i = 0; i < n; i++) cin >> a[i].fi >> a[i].se;
sort(a, a + n, [](pair<int, int> a, pair<int, int> b){
return a.se == b.se ? a.fi < b.fi : a.se < b.se;
});
int res = 0;
multiset<int> s;
for (int i = 0; i < k; i++) s.insert(0);
for (int i = 0; i < n; i++){
auto it = s.upper_bound(a[i].fi);
if (it == s.begin()) continue;
else s.erase(--it), s.insert(a[i].se), res++;
}
cout << res;
return 0;
}

Maximum Subarray Sum II


Solution:

Tính tổng các prefix sum của mảng a và lưu vô mảng khác

Sử dụng multiset để tìm giá trị tổng lớn nhất trong đoạn a, b

Xóa các phần tử nằm trước đoạn a

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n, l, r; cin >> n >> l >> r;
int a[n + 1], p[n + 1] = {};
for (int i = 1; i <= n; i++) cin >> a[i], p[i] = a[i] + p[i - 1];
int res = -1e18;
multiset<int> s;
for (int i = l; i <= n; i++){
if (i > r) s.erase(s.find(p[i - r - 1]));
s.insert(p[i - l]);
res = max(p[i] - *s.begin(), res);
}
cout << res;
return 0;
}

Dynamic Programming
Dice Combinations
Solution:

Sử dụng quy hoạch động knapsack

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

#define M 1000000007

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n; cin >> n;
int dp[n + 1] = {};
dp[0] = 1;
for (int i = 1; i <= n; i++){
for (int j = 1; j <= 6; j++){
if (i - j >= 0) dp[i] = (dp[i] + dp[i - j]) % M;
}
}
cout << dp[n] % M;
return 0;
}

Minimizing Coins
Solution:

Sử dụng quy hoạch động knapsack, và 1 số đk để tối ưu

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n, x; cin >> n >> x;
int a[n];
for (int i = 0; i < n; i++) cin >> a[i];
int dp[x + 1] = {};
for (int i = 0; i < n; i++){
for (int j = a[i]; j <= x; j++){
if ((dp[j - a[i]] && dp[j] == 0) || j == a[i]) dp[j] = dp[j -
a[i]] + 1;
else if (dp[j - a[i]]) dp[j] = min(dp[j], dp[j - a[i]] + 1);
}
}
if (dp[x] == 0) cout << "-1\n";
else cout << dp[x] << "\n";
return 0;
}

Coin Combinations I
Solution:

Sử dụng dp knapsack

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

#define M 1000000007

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n, x, res = 0; cin >> n >> x;
int a[n];
for (int i = 0; i < n; i++) cin >> a[i];
int dp[x + 1] = {};
dp[0] = 1;
for (int j = 0; j <= x; j++){
for (int i = 0; i < n; i++){
if (j >= a[i]) dp[j] += dp[j - a[i]] % M;
}
}
cout << dp[x] % M;
return 0;
}

Coin Combinations II
Solution:
Sử dụng dp knapsack
Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

#define M 1000000007

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n, x, res = 0; cin >> n >> x;
int a[n];
for (int i = 0; i < n; i++) cin >> a[i];
int dp[x + 1] = {};
dp[0] = 1;
for (int i = 0; i < n; i++){
for (int j = a[i]; j <= x; j++){
dp[j] += dp[j - a[i]] % M;
}
}
cout << dp[x] % M;
return 0;
}

Removing Digits

Solution:

Sử dụng vòng lặp while để xóa số lớn nhất của số n cho tới khi n = 0

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n, res = 0; cin >> n;
while (n > 0){
string s = to_string(n);
int m = 0;
for (char i : s) m = max((int) i - '0', m);
n -= m, res++;
}
cout << res;
return 0;
}

Grid Paths

Solution:

Sử dụng quy hoạch động đường đi trên lưới, với dp[i][j] là tổng lớn nhất tại I, j

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

const int M = 1e9 + 7;


signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n; cin >> n;
char a[n][n];
int dp[1005][1005] = {};
for (int i = 0; i < n; i++) for (int j = 0; j < n; j++){
cin >> a[i][j];
}
for (int i = 0; i < n; i++){
for (int j = 0; j < n; j++){
if (a[i][j] == '*') continue;
if (i == 0 && j == 0) dp[i][j] = 1;
else if (i == 0){
if (a[i][j - 1] == '.') dp[i][j] += dp[i][j - 1] % M;
}
else if (j == 0){
if (a[i - 1][j] == '.') dp[i][j] += dp[i - 1][j] % M;
}
else{
if (a[i - 1][j] == '.') dp[i][j] += dp[i - 1][j] % M;
if (a[i][j - 1] == '.') dp[i][j] += dp[i][j - 1] % M;
}
}
}
cout << dp[n - 1][n - 1] % M;
return 0;
}

Array Description

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

const int M = 1e9 + 7;


signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n, m; cin >> n >> m;
int a[n];
for (int &i : a) cin >> i;
int dp[n + 1][m + 1] = {};
if (a[0]) dp[0][a[0]] = 1;
else for (int i = 1; i <= m; i++) dp[0][i] = 1;
for (int i = 1; i < n; i++){
if (a[i] == 0){
for (int j = 1; j <= m; j++){
(dp[i][j] += dp[i - 1][j]) %= M;
if (j > 1) (dp[i][j] += dp[i - 1][j - 1]) %= M;
if (j < m) (dp[i][j] += dp[i - 1][j + 1]) %= M;
}
}
else{
(dp[i][a[i]] += dp[i - 1][a[i]]) %= M;
if (a[i] > 1) (dp[i][a[i]] += dp[i - 1][a[i] - 1]) %= M;
if (a[i] < m) (dp[i][a[i]] += dp[i - 1][a[i] + 1]) %= M;
}
}
int res = 0;
for (int i = 1; i <= m; i++) (res += dp[n - 1][i]) %= M;
cout << res;
return 0;
}

Counting Towers

Solution:
Tạo 2 mảng join[n] và sep[n], join biểu diễn tầng thứ I có 2 ô vuông dính liền nhau, sep biểu diễn
tầng thứ I có 2 ô vuông tách rời nhau

Cập nhật join[i] bằng ct join[i] = 2 join[I – 1] + sep[I – 1], vì khi hàng thứ I có 2 ô vuông dính nhau có
thể tạo nên hàn thứ I bằng 2 cách có hàng trước đó có ô vuông dính nhau, và 1 cách hàng trước đó
có 2 ô vuông rời nhau

Cập nhật sep[i] bằng ct join[i] = 4 sep[I – 1] + join[I – 1], vì khi hàng thứ I có 2 ô vuông dính nhau có
thể tạo nên hàn thứ I bằng 4 cách có hàng trước đó có ô vuông rời nhau, và 1 cách hàng trước đó có
2 ô dính rời nhau

Kết quả sẽ là join[n] + sep[n]

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long


#define fi first
#define se second
#define pb push_back
#define ii pair<int, int>
#define For(i, a, b, c) for (int i = a; i < b; i += c)
#define mod 1000000007

int dp[1000005] = {};


void solve()
{
int n; cin >> n;
if (dp[n]){
cout << dp[n] << "\n";
return;
}
int join[n], sep[n];
join[0] = 1, sep[0] = 1;
For(i, 1, n, 1){
join[i] = (2 * join[i - 1]) % mod + sep[i - 1] % mod;
join[i] %= mod;
sep[i] = (4 * sep[i - 1]) % mod + join[i - 1] % mod;
sep[i] %= mod;
}
dp[n] = (join[n - 1] + sep[n - 1]) % mod;
cout << dp[n] << "\n";
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int t; cin >> t;
while (t--) solve();
return 0;
}

Edit Distance

Solution:

Tạo mảng dp[i][j] với I, j là vị trí của xâu a và b


Khi đó, khi cộng 1 kí tự vào xâu dp[i][j] sẽ bằng dp[I – 1][j] + 1

Xóa 1 kí tự vào xâu dp[i][j] sẽ bằng dp[I – 1][j] + 1

Thay thế 1 kí tự vào xâu dp[i][j] sẽ bằng dp[I - 1][j - 1] + 1

Cập nhật dp[i][j] theo min của 3 cách trên

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);
string a, b; cin >> a >> b;
int dp[a.size() + 1][b.size() + 1] = {};
for (int i = 0; i <= a.size(); i++){
for (int j = 0; j <= b.size(); j++){
if (i == 0) dp[i][j] = j;
else if (j == 0) dp[i][j] = i;
else if (a[i - 1] == b[j - 1]) dp[i][j] = dp[i - 1][j - 1];
else dp[i][j] = min({dp[i - 1][j], dp[i][j - 1], dp[i - 1][j -
1]}) + 1;
}
}
cout << dp[a.size()][b.size()];
return 0;
}

Rectangle Cutting
Solution:

Sử dụng quy hoạch động với đệ quy có nhớ

Dùng vòng lặp for để và gọi đệ quy khi cắt theo hàng ngang và hàng dọc

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

int a, b;
int dp[505][505] = {};
int dq(int n, int m)
{
if (n == m) return 1;
if (dp[n][m] != -1) return dp[n][m];
int m1 = INT_MAX, m2 = INT_MAX;
for (int i = 1; i <= n / 2; i++) m1 = min(dq(i, m) + dq(n - i, m), m1);
for (int i = 1; i <= m / 2; i++) m2 = min(dq(n, i) + dq(n, m - i), m2);
dp[n][m] = min(m1, m2);
return dp[n][m];
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);
cin >> a >> b;
memset(dp, -1, sizeof(dp));
cout << dq(a, b) - 1;
return 0;
}

Money Sums
Solution:

Sử dung quy hoạch động knapsack

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n, s = 0; cin >> n;
int a[n];
for (int &i : a) cin >> i, s += i;
int dp[s + 1] = {};
dp[0] = 1;
set<int> res;
for (int i = 0; i < n; i++){
for (int j = s; j >= a[i]; j--){
if (dp[j - a[i]]) dp[j] = 1, res.insert(j);
}
}
cout << res.size() << "\n";
for (int i : res) cout << i << " ";
return 0;
}

Removal Game
Solution:

Sử dụng quy hoạch động với đệ quy có nhớ

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

int n;
int a[5005], dp[5005][5005];
int dq(int l, int r)
{
if (l > r) return 0;
if (dp[l][r] != -1) return dp[l][r];
int m1, m2;
m1 = a[l] + min(dq(l + 2, r), dq(l + 1, r - 1));
m2 = a[r] + min(dq(l, r - 2), dq(l + 1, r - 1));
return dp[l][r] = max(m1, m2);
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin >> n;
for (int i = 0; i < n; i++) cin >> a[i];
memset(dp, -1, sizeof(dp));
cout << dq(0, n - 1);
return 0;
}

Two Sets II

Solution:

Sử dụng quy hoạch động knapsack để tìm các tổng có thể tạo bằng các số từ 1 đến n

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

const int M = 1e9 + 7;


signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n; cin >> n;
int s = n * (n + 1) / 2;
if (s % 2 != 0){cout << 0; return 0;}
int dp[s + 1] = {};
dp[0] = 1;
for (int i = 1; i <= n; i++){
for (int j = s; j >= i; j--) dp[j] += dp[j - i] %= M;
}
cout << dp[s / 2] / 2;
return 0;
}

Increasing Subsequence
Solution:
Áp dụng quy hoạch động dãy con tăng nâng cao.
Sử dụng lower_bound để tìm các số nhỏ hơn bằng a[i]
Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n, res = 0; cin >> n;
int a[n];
for (int &i : a) cin >> i;
vector<int> v;
for (int i = 0; i < n; i++){
auto it = lower_bound(v.begin(), v.end(), a[i]);
if (it == v.end()) v.push_back(a[i]);
else *it = a[i];
}
cout << v.size();
return 0;
}

Projects
Solution:
Sắp xếp mảng a theo thời gian kết thúc tang dần
Dùng tìm kiếm nhị phân để tìm số phần tử bé hơn thời gian bắt đầu của a[i]
Dùng quy hoạch động để cập nhật số tiền lớn nhất

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

struct project{
int st, fn, mn;
};
int binarysearch(project a[], int l, int r, int x)
{
int res = -1;
while (l <= r){
int m = (l + r) / 2;
if (a[m].fn < x){
if (a[m + 1].fn < x) l = m + 1;
else return m;
}
else r = m - 1;
}
return res;
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n; cin >> n;
project a[n];
for (int i = 0; i < n; i++) cin >> a[i].st >> a[i].fn >> a[i].mn;
sort(a, a + n, [](project a, project b){
return a.fn < b.fn;
});
int dp[n + 1] = {};
dp[0] = a[0].mn;
for (int i = 1; i < n; i++){
int t = a[i].mn, j = binarysearch(a, 0, i - 1, a[i].st);
if (j != -1) t += dp[j];
dp[i] = max(dp[i - 1], t);
}
cout << dp[n - 1];
return 0;
}
Graph Algorithms

Counting Rooms
Solution:
Sử dụng đệ quy.
Nếu có ô nào chưa đc đánh dấu thì tăng kết quả thêm 1 và dung đệ quy để đánh dấu các căn phòng
đã kt
Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

int n, m, res = 0;
char a[1005][1005];
void dq(int i, int j)
{
if (i < 1 || i > n || j < 1 || j > m) return;
a[i][j] = '#';
if (a[i - 1][j] == '.') dq(i - 1, j);
if (a[i + 1][j] == '.') dq(i + 1, j);t
if (a[i][j - 1] == '.') dq(i, j - 1);
if (a[i][j + 1] == '.') dq(i, j + 1);
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++){
cin >> a[i][j];
}
for (int i = 1; i <= n; i++){
for (int j = 1; j <= m; j++){
if (a[i][j] == '.'){dq(i, j); res++;}
}
}
cout << res;
return 0;
}

Labyrinth
Solution:
Sử dụng bfs và hàng đợi queue để tìm đường đi ngắn nhất qua các ô
Dùng truy vết để tìm lại đường đi
Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

#define fi first
#define se second

int n, m;
char a[1005][1005];
int mark[1005][1005] = {};
int dx[4] = {-1, 0, 1, 0};
int dy[4] = {0, -1, 0, 1};
string s = "ULDR";
signed main()
{
cin >> n >> m;
pair<int, int> begin, end;
for (int i = 0; i < n; i++) for (int j = 0; j < m; j++){
cin >> a[i][j];
if (a[i][j] == 'A') begin.fi = i, begin.se = j;
if (a[i][j] == 'B') end.fi = i, end.se = j;
}
queue<pair<int, int>> q;
int pre[1005][1005];
q.push({begin.fi, begin.se});
mark[begin.fi][begin.se] = 1;
while (!q.empty()){
pair<int, int> f = q.front(); q.pop();
for (int i = 0; i < 4; i++){
pair<int, int> u = {f.fi + dx[i], f.se + dy[i]};
if (u.fi < 0 || u.fi >= n || u.se < 0 || u.se >= m) continue;
if (a[u.fi][u.se] == '#' || mark[u.fi][u.se]) continue;
mark[u.fi][u.se] = 1;
pre[u.fi][u.se] = i;
q.push(u);
}
}
if (mark[end.fi][end.se]){
cout << "YES\n";
vector<int> path;
while (end != begin){
int p = pre[end.fi][end.se];
path.push_back(p);
end = {end.fi - dx[p], end.se - dy[p]};
}
reverse(path.begin(), path.end());
cout << path.size() << "\n";
for (int i : path) cout << s[i];
}
else cout << "NO";
return 0;
}

Building Roads
Solution:
Sử dụng dfs để đánh dấu các thành phần liên thông
Sử dụng vector để lưu các đường đi giữa các thành phần ko liên thông
Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

int n, m;
vector<vector<int>> a(100001);
int gone[100005] = {};
void dfs(int i)
{
gone[i] = 1;
for (int k : a[i]){
if (!gone[k]) dfs(k);
}
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin >> n >> m;
for (int i = 0, x, y; i < m; i++){
cin >> x >> y;
a[x].push_back(y);
a[y].push_back(x);
}
vector<int> res;
for (int i = 1; i <= n; i++){
if (!gone[i]){
res.push_back(i);
dfs(i);
}
}
cout << res.size() - 1 << "\n";
for (int i = 0; i < res.size() - 1; i++) cout << res[i] << " " << res[i
+ 1] << "\n";
return 0;
}

Message Route
Solution:
Sử dụng bfs để tìm đường đi từ 1 đến n
Nếu có đường đi thì truy vết để lưu đường đi ngắn nhất
Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

int n, m;
vector<vector<int>> a(200005);
vector<int> res;
int gone[200005] = {};
void bfs(int x, int y)
{
vector<int> pr(n, -1);
queue<int> q;
q.push(x);
gone[x] = 1;
while (!q.empty()){
int f = q.front();
q.pop();
if (f == y){
vector<int> path;
int node = y;
while (node != -1){
path.push_back(node);
node = pr[node];
}
if (res.empty() || path.size() < res.size()) res = path;
return;
}
for (int i : a[f]){
if (!gone[i]) q.push(i), gone[i] = 1, pr[i] = f;
}
}
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin >> n >> m;
for (int i = 0, x, y; i < m; i++){
cin >> x >> y;
a[x].push_back(y);
a[y].push_back(x);
}
bfs(1, n);
if (!gone[n]) cout << "IMPOSSIBLE";
else{
cout << res.size() << "\n";
for (int i = res.size() - 1; i >= 0; i--) cout << res[i] << " ";
}
return 0;
}

Building Teams
Solution:
Nếu có người nào không trong đội, sử dụng bfs để đánh dấu những người khác đội
Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

int n, m;
vector<vector<int>> a(200005);
int team[200005] = {};
bool check = true;
void bfs(int x)
{
queue<int> q; q.push(x);
team[x] = 1;
while (!q.empty()){
int f = q.front(); q.pop();
for (int i : a[f]){
if (!team[i]) q.push(i), team[i] = 3 - team[f];
else if (team[i] == team[f]){
check = false;
return;
}
}
}
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin >> n >> m;
for (int i = 0, x, y; i < m; i++){
cin >> x >> y;
a[x].push_back(y);
a[y].push_back(x);
}
for (int i = 1; i <= n; i++){
if (!team[i]) bfs(i);
}
if (!check) cout << "IMPOSSIBLE";
else for (int i = 1; i <= n; i++) cout << team[i] << " ";
return 0;
}

Round Trip
Solution:
Sử dụng dfs để duyệt các cách đỉnh của đồ thị, nếu khi đang duyệt gặp 1 đỉnh đã đi qua thì
dung truy vết để xây dựng đường đi.
Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

int n, m;
vector<vector<int>> a(200005);
int mark[200005] = {}, p[200005];
void dfs(int u)
{
mark[u] = 1;
for (int v : a[u]){
if (mark[v] == 0){
p[v] = u;
dfs(v);
}
else if (mark[v] == 1){
vector<int> path;
path.push_back(u);
while (path.back() != v) path.push_back(p[path.back()]);
path.push_back(u);
if (path.size() <= 3) continue;
reverse(path.begin(), path.end());
cout << path.size() << "\n";
for (int i : path) cout << i << " ";
exit(0);
}
}
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin >> n >> m;
for (int i = 0, x, y; i < m; i++){
cin >> x >> y;
a[x].push_back(y);
a[y].push_back(x);
}
for (int i = 1; i <= n; i++){
if (!mark[i]) dfs(i);
}
cout << "IMPOSSIBLE";
return 0;
}

Monsters
Solution:
Dùng bfs cho người chơi để tìm thời gian ngắn nhất để người chơi đi đến các ô trong mê
cung
Dùng bfs cho quái vật để tìm thời gian ngắn nhất để quái vật đi đến các ô trong mê cung
Duyệt qua các cạnh ở ngoài cùng của mê cung mà có lối ra nếu thời gian đi đến ô đó ít hơn
thời gian quái vật đến ô đó thì kết quả là đường đi đến ô đó của người chơi bằng cách sử
dụng truy vết

Code:
#include "bits/stdc++.h"

using namespace std;

#define int long long

#define fi first
#define se second

int n, m;
char a[1005][1005];
pair<int, int> start;
queue<pair<int, int>> monster, player;
string s = "ULDR";
int dx[4] = {-1, 0, 1, 0};
int dy[4] = {0, -1, 0, 1};
int monsterdist[1005][1005], playerdist[1005][1005];
int monstermark[1005][1005] = {}, playermark[1005][1005] = {};
int pre[1005][1005];
void bfsmonster()
{
while (!monster.empty()){
pair<int, int> f = monster.front(); monster.pop();
monstermark[f.fi][f.se] = 1;
for (int i = 0; i < 4; i++){
pair<int, int> u = {f.fi + dx[i], f.se + dy[i]};
if (u.fi < 0 || u.fi >= n || u.se < 0 || u.se >= m) continue;
if (a[u.fi][u.se] == '#' || monstermark[u.fi][u.se]) continue;
monstermark[u.fi][u.se] = 1;
if (monsterdist[u.fi][u.se] == -1) monsterdist[u.fi][u.se] =
monsterdist[f.fi][f.se] + 1;
else monsterdist[u.fi][u.se] = min(monsterdist[f.fi][f.se] + 1,
monsterdist[u.fi][u.se]);
monster.push({u.fi, u.se});
}
}
}
void bfsplayer()
{
while (!player.empty()){
pair<int, int> f = player.front(); player.pop();
playermark[f.fi][f.se] = 1;
for (int i = 0; i < 4; i++){
pair<int, int> u = {f.fi + dx[i], f.se + dy[i]};
if (u.fi < 0 || u.fi >= n || u.se < 0 || u.se >= m) continue;
if (a[u.fi][u.se] == '#' || playermark[u.fi][u.se]) continue;
playermark[u.fi][u.se] = 1;
playerdist[u.fi][u.se] = playerdist[f.fi][f.se] + 1;
player.push({u.fi, u.se});
pre[u.fi][u.se] = i;
}
}
}
void trace(int i, int j)
{
vector<int> path;
pair<int, int> end = {i, j};
while (end != start){
int p = pre[end.fi][end.se];
path.push_back(p);
end = {end.fi - dx[p], end.se - dy[p]};
}
reverse(path.begin(), path.end());
cout << "YES\n" << path.size() << "\n";
for (int i : path) cout << s[i];
exit(0);
}
void solve()
{
for (int i = 0; i < n; i++){
if (a[i][0] == 'A'){cout << "YES\n0"; exit(0);}
if (a[i][0] == '.' && playerdist[i][0] != -1 && (playerdist[i][0] <
monsterdist[i][0] || monsterdist[i][0] == -1)) trace(i, 0);
}
for (int i = 0; i < n; i++){
if (a[i][m - 1] == 'A'){cout << "YES\n0"; exit(0);}
if (a[i][m - 1] == '.' && playerdist[i][m - 1] != -1 &&
(playerdist[i][m - 1] < monsterdist[i][m - 1] || monsterdist[i][m - 1] == -
1)) trace(i, m - 1);
}
for (int i = 0; i < m; i++){
if (a[0][i] == 'A'){cout << "YES\n0"; exit(0);}
if (a[0][i] == '.' && playerdist[0][i] != -1 && (playerdist[0][i] <
monsterdist[0][i] || monsterdist[0][i] == -1)) trace(0, i);
}
for (int i = 0; i < m; i++){
if (a[n - 1][i] == 'A'){cout << "YES\n0"; exit(0);}
if (a[n - 1][i] == '.' && playerdist[n - 1][i] != -1 &&
(playerdist[n - 1][i] < monsterdist[n - 1][i] || monsterdist[n - 1][i] == -
1)) trace(n - 1, i);
}
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin >> n >> m;
memset(monsterdist, -1, sizeof(monsterdist));
memset(playerdist, -1, sizeof(playerdist));
for (int i = 0; i < n; i++) for (int j = 0; j < m; j++){
cin >> a[i][j];
if (a[i][j] == 'M') monster.push({i, j}), monsterdist[i][j] = 0;
if (a[i][j] == 'A') player.push({i, j}), start = {i, j},
playerdist[i][j] = 0;;
}
bfsmonster();
bfsplayer();
solve();
cout << "NO";
return 0;
}

Shortest Routes I
Solution:
Sử dụng thuật toán Dijkstra để tìm đường đi ngắn nhất
Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

#define fi first
#define se second

const int inf = 1e18;

int n, m;
vector<pair<int, int>> a[200005];
int dist[200005], mark[200005] = {};
void dijkstra(int x)
{
fill(dist + 1, dist + n + 1, inf);
dist[x] = 0;
priority_queue<pair<int, int>, vector<pair<int, int>>,
greater<pair<int, int>>> pq;
pq.push({0, x});
while (!pq.empty()){
int u = pq.top().se; pq.pop();
if (mark[u]) continue;
mark[u] = 1;
for (auto i : a[u]){
int v = i.fi, w = i.se;
if (dist[v] > dist[u] + w){
dist[v] = dist[u] + w;
pq.push({dist[v], v});
}
}
}
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin >> n >> m;
for (int i = 0, x, y, z; i < m; i++){
cin >> x >> y >> z;
a[x].push_back({y, z});
}
dijkstra(1);
for (int i = 1; i <= n; i++){
cout << dist[i] << " ";
}
return 0;
}

Shortest Routes II
Solution:
Sử dụng thuật toán Floy Warshall để tìm đường đi ngắn nhất đến tất cả thành phố từ 1 đến n
Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

const int inf = 1e18;


signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n, m, q; cin >> n >> m >> q;
vector<vector<int>> a;
for (int i = 0, x, y, w; i < m; i++){
cin >> x >> y >> w;
a.push_back({x, y, w});
}
int dis[n + 1][n + 1];
for (int i = 1; i <= n; i++){
for (int j = 1; j <= n; j++){
dis[i][j] = inf;
if (i == j) dis[i][j] = 0;
}
}
for (auto i : a){
int x = i[0], y = i[1], w = i[2];
dis[x][y] = min(w, dis[x][y]);
dis[y][x] = min(w, dis[x][y]);
}
for (int i = 1; i <= n; i++){
for (int j = 1; j <= n; j++){
for (int k = 1; k <= n; k++){
dis[j][k] = min(dis[j][i] + dis[i][k], dis[j][k]);
}
}
}
while (q--){
int x, y; cin >> x >> y;
cout << (dis[x][y] == inf ? -1 : dis[x][y]) << "\n";
}
return 0;
}

Flight Discount
Solution:
Sử dụng thuật toán Dijkstra theo 2 chiều từ 1 và từ n
Duyệt qua mảng và tìm giá trị nhỏ nhất khi giảm chi phí của phần tử I đi một nữa
Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

#define fi first
#define se second

const int inf = 1e18;

int n, m;
vector<pair<int, int>> a[200005], b[200005];
int dist1[200005], dist2[200005], mark[200005] = {};
void dijkstra(int x, vector<pair<int, int>> a[], int dist[])
{
fill(dist + 1, dist + n + 1, inf);
dist[x] = 0;
priority_queue<pair<int, int>, vector<pair<int, int>>,
greater<pair<int, int>>> pq;
pq.push({0, x});
while (!pq.empty()){
int u = pq.top().se; pq.pop();
if (mark[u]) continue;
mark[u] = 1;
for (auto i : a[u]){
int v = i.fi, w = i.se;
if (dist[v] > dist[u] + w){
dist[v] = dist[u] + w;
pq.push({dist[v], v});
}
}
}
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin >> n >> m;
vector<pair<pair<int, int>, int>> v;
for (int i = 0, x, y, w; i < m; i++){
cin >> x >> y >> w;
v.push_back({{x, y}, w});
a[x].push_back({y, w});
b[y].push_back({x, w});
}
dijkstra(1, a, dist1);
memset(mark, 0, sizeof(mark));
dijkstra(n, b, dist2);
int res = inf;
for (int i = 0; i < m; i++) res = min(dist1[v[i].fi.fi] + v[i].se / 2 +
dist2[v[i].fi.se], res);
cout << res;
return 0;
}

Cycle Finding
Solution:
Sử dụng thuật toán Bellman-Ford trong n – 1 lần để tìm đường đi ngắn nhất đến các đỉnh
Duyệt qua mảng 1 lần nữa, nếu có thể tiếp tục tối ưu thì trong đồ thị đã có chu trình âm, gán 1 node
là 1 đỉnh trong chu trình âm, là tìm chu trình đó.
Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long


#define fi first
#define se second
#define ii pair<int, int>
#define pb push_back

const int inf = 1e9 + 7;

int n, m;
vector<vector<int>> a;
int dist[5000], p[5000] = {};
void bellmanford()
{
fill(dist, dist + n + 1, inf);
dist[1] = 0;
for (int i = 0; i < n; i++){
for (auto e : a){
int u = e[0], v = e[1], w = e[2];
if (dist[u] + w < dist[v]){
dist[v] = dist[u] + w;
p[v] = u;
}
}
}
}
void findnegcycle()
{
int negcycle_start = -1;
for (auto e : a){
int u = e[0], v = e[1], w = e[2];
if (dist[u] + w < dist[v]){
dist[v] = -inf;
negcycle_start = v;
}
}
if (negcycle_start == -1){
cout << "NO"; return;
}
for (int i = 0; i < n; i++)
negcycle_start = p[negcycle_start];
int node = negcycle_start;
vector<int> path;
path.pb(node);
node = p[node];
while (node != negcycle_start){
path.pb(node);
node = p[node];
}
path.pb(negcycle_start);
reverse(path.begin(), path.end());
cout << "YES\n";
for (int i : path) cout << i << " ";
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin >> n >> m;
for (int i = 0, u, v, w; i < m; i++){
cin >> u >> v >> w;
a.pb({u, v, w});
}
bellmanford();
findnegcycle();
return 0;
}

Round Trip II

Solution:

Sử dụng thuật toán sắp xếp topo

Dùng dfs để duyệt qua các đỉnh trên đồ thị


Đánh dẩu đỉnh đó bằng 0 nếu chưa được duyệt, 1 nếu đang trong quá trình
duyệt, và 2 nếu đã duyệt xong

Nếu đang trong quá trình duyệt, gặp 1 đỉnh có đánh dấu bằng 1, tức là đỉnh đó
cũng đang trong quá trình duyệt, thì đã đi được 1 vòng tròn. Dùng truy vết để
tìm chu trình đó

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

int n, m;
vector<int> a[200005];
int mark[200005] = {}, p[200005] = {};
void dfs(int x)
{
mark[x] = 1;
for (int i : a[x]){
if (mark[i] == 1){
vector<int> path = {x};
while (path.back() != i){
path.push_back(p[path.back()]);
}
path.push_back(x);
reverse(path.begin(), path.end());
cout << path.size() << "\n";
for (int i : path) cout << i << " ";
exit(0);
}
if (!mark[i]){
p[i] = x;
dfs(i);
}
}
mark[x] = 2;
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin >> n >> m;
for (int i = 0, x, y; i < m; i++){
cin >> x >> y;
a[x].push_back(y);
}
for (int i = 1; i <= n; i++){
if (!mark[i]){p[i] = i; dfs(i);}
}
cout << "IMPOSSIBLE";
return 0;
}

Course Schedule

Solution:

Sử dụng thuật toán sắp xếp topo.


Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

int n, m;
vector<int> a[200005], topo;
queue<int> source;
int mark[200005];
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin >> n >> m;
for (int i = 0, x, y; i < m; i++){
cin >> x >> y;
a[x].push_back(y), mark[y]++;
}
for (int i = 1; i <= n; i++) if (!mark[i]) source.push(i);
while (!source.empty()){
int f = source.front(); source.pop();
topo.push_back(f);
for (int i : a[f]){
mark[i]--;
if (!mark[i]) source.push(i);
}
}
if (topo.size() < n){cout << "IMPOSSIBLE"; return 0;}
for (int i : topo) cout << i << " ";
return 0;
}

Longest Flight Route

Solution:

Sử dụng thuật toán sắp xếp topo, Kahn’s algorithm 2 lần

Lần 1 để loại bỏ những đường đi từ các đỉnh khác 1

Sau đó chạy bfs từ đỉnh 1 để tìm đường đi dài nhất

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

int n, m;
vector<int> a[200005];
int mark[200005] = {}, p[200005] = {};
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin >> n >> m;
for (int i = 0, x, y; i < m; i++){
cin >> x >> y;
a[x].push_back(y);
mark[y]++;
}
queue<int> q;
for (int i = 2; i <= n; i++) if (!mark[i]) q.push(i);
while (!q.empty()){
int f = q.front(); q.pop();
if (f == 1) continue;
for (int i : a[f]){
mark[i]--;
if (!mark[i]) q.push(i);
}
}
q.push(1);
while (!q.empty()){
int f = q.front(); q.pop();
for (int i : a[f]){
mark[i]--;
if (!mark[i]){
p[i] = f;
q.push(i);
}
}
}
vector<int> path;
while (n){
path.push_back(n);
n = p[n];
}
if (path.size() <= 1){cout << "IMPOSSIBLE"; return 0;}
reverse(path.begin(), path.end());
cout << path.size() << "\n";
for (int i : path) cout << i << " ";
return 0;
}

Game Routes

Solution:

Sử dụng thuật toán sắp xếp topo kết hợp với quy hoạc động

Khi chạy bfs, dùng quy hoạc động để cộng cho số cách của đỉnh đi tới được đỉnh đang duyệt

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

int n, m;
vector<int> a[200005], back[200005];
int mark[200005] = {};
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin >> n >> m;
for (int i = 0, x, y; i < m; i++){
cin >> x >> y;
a[x].push_back(y), back[y].push_back(x);
mark[y]++;
}
int dp[n + 1] = {};
dp[1] = 1;
queue<int> q;
for (int i = 1; i <= n; i++) if (!mark[i]) q.push(i);
while (!q.empty()){
int f = q.front(); q.pop();
for (int i : a[f]){
mark[i]--;
if (!mark[i]) q.push(i);
}
for (int i : back[f]) (dp[f] += dp[i]) %= 1000000007;
}
cout << dp[n];
return 0;
}

Road Construction
Solution:

Sử dụng thuật toán disjoint set union

Khi kết nói 2 đồ thị với nhau số thành phần sẽ giảm đi 1, và kích thước của thành phần lớn sẽ được
cập nhật trong khi dùng dsu

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

int n, m, cnt, mx = 0;
vector<int> a[200005];
int p[200005] = {}, sz[200005] = {};
void make_set(int u)
{
p[u] = u, sz[u] = 1;
}
int find_set(int u)
{
if (u == p[u]) return u;
return p[u] = find_set(p[u]);
}
void union_sets(int a, int b)
{
a = find_set(a), b = find_set(b);
if (a != b){
if (sz[a] < sz[b]) swap(a, b);
p[b] = a;
sz[a] += sz[b];
cnt--, mx = max(sz[a], mx);
}
cout << cnt << " " << mx << "\n";
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin >> n >> m;
cnt = n;
for (int i = 1; i <= n; i++) make_set(i);
while (m--){
int x, y; cin >> x >> y;
union_sets(x, y);
}
return 0;
}

Range Queries

Static Range Sum Queries

Solution:

Sử dụng 1 mảng để lưu các prefix sum, khi đó tổng từ a đến b là prefixsum[b] – prefixsum[a – 1]

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n, q; cin >> n >> q;
int a[n], ps[n] = {};
for (int i = 1; i <= n; i++) cin >> a[i], ps[i] = ps[i - 1] + a[i];
while (q--){
int a, b; cin >> a >> b;
cout << ps[b] - ps[a - 1] << "\n";
}
return 0;
}

Static Range Minimum Queries

Solution:

Sử dụng segment tree để lấy giá trị min trong đoạn

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

int n, q;
int a[200005], tree[800005];
void build(int k, int l, int r)
{
if (l == r){
tree[k] = a[l];
return;
}
int m = (l + r) / 2;
build(k * 2, l, m);
build(k * 2 + 1, m + 1, r);
tree[k] = min(tree[k * 2], tree[k * 2 + 1]);
}
int get(int k, int a, int b, int l, int r)
{
if (a > r || b < l) return LLONG_MAX;
if (a <= l && b >= r) return tree[k];
int m = (l + r) / 2;
return min(get(k * 2, a, b, l, m), get(k * 2 + 1, a, b, m + 1, r));
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin >> n >> q;
for (int i = 0; i < n; i++) cin >> a[i];
build(1, 0, n - 1);
while (q--){
int a, b; cin >> a >> b;
cout << get(1, a - 1, b - 1, 0, n - 1) << "\n";
}
return 0;
}

Dynamic Range Sum Queries

Solution :

Sử dụng segment tree để lấy giá trị của tổng trong đoạn và cập nhật giá trị

Code :
#include "bits/stdc++.h"
using namespace std;

#define int long long

int n, q;
int a[200005], tree[800005];
void build(int k, int l, int r)
{
if (l == r){
tree[k] = a[l];
return;
}
int m = (l + r) / 2;
build(k * 2, l, m);
build(k * 2 + 1, m + 1, r);
tree[k] = tree[k * 2] + tree[k * 2 + 1];
}
void update(int k, int i, int v, int l, int r)
{
if (i < l || i > r) return;
if (l == r){
tree[k] = v;
return;
}
int m = (l + r) / 2;
update(k * 2, i, v, l, m);
update(k * 2 + 1, i, v, m + 1, r);
tree[k] = tree[k * 2] + tree[k * 2 + 1];
}
int get(int k, int a, int b, int l, int r)
{
if (a > r || b < l) return 0;
if (a <= l && b >= r) return tree[k];
int m = (l + r) / 2;
return get(k * 2, a, b, l, m) + get(k * 2 + 1, a, b, m + 1, r);
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin >> n >> q;
for (int i = 0; i < n; i++) cin >> a[i];
build(1, 0, n - 1);
while (q--){
int x, y, z; cin >> x >> y >> z;
if (x == 1) update(1, y - 1, z, 0, n - 1);
else cout << get(1, y - 1, z - 1, 0, n - 1) << "\n";
}
return 0;
}

Dynamic Range Minimum Queries

Solution:

Sử dụng segment tree để lấy giá trị min trong đoạn và cập nhật giá trị

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

int n, q;
int a[200005], tree[800005];
void build(int k, int l, int r)
{
if (l == r){
tree[k] = a[l];
return;
}
int m = (l + r) / 2;
build(k * 2, l, m);
build(k * 2 + 1, m + 1, r);
tree[k] = min(tree[k * 2], tree[k * 2 + 1]);
}
void update(int k, int i, int v, int l, int r)
{
if (i < l || i > r) return;
if (l == r){
tree[k] = v;
return;
}
int m = (l + r) / 2;
update(k * 2, i, v, l, m);
update(k * 2 + 1, i, v, m + 1, r);
tree[k] = min(tree[k * 2], tree[k * 2 + 1]);
}
int get(int k, int a, int b, int l, int r)
{
if (a > r || b < l) return LLONG_MAX;
if (a <= l && b >= r) return tree[k];
int m = (l + r) / 2;
return min(get(k * 2, a, b, l, m), get(k * 2 + 1, a, b, m + 1, r));
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin >> n >> q;
for (int i = 0; i < n; i++) cin >> a[i];
build(1, 0, n - 1);
while (q--){
int x, y, z; cin >> x >> y >> z;
if (x == 1) update(1, y - 1, z, 0, n - 1);
else cout << get(1, y - 1, z - 1, 0, n - 1) << "\n";
}
return 0;
}

Range Xor Queries

Solution:

Sử dụng 1 mảng để lưu các prefix xor, khi đó tổng từ a đến b là prefixxor[b] – prefixxor[a – 1]

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n, q; cin >> n >> q;
int a[n + 1], p[n + 1] = {};
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++) p[i] = p[i - 1] ^ a[i];
while (q--){
int i, j; cin >> i >> j;
cout << (p[j] ^ p[i - 1]) << "\n";
}
return 0;
}

Range Update Queries

Solution :

Sử dụng segment tree và thuật toán lazy propagation dể cập nhật đoạn

Code :
#include "bits/stdc++.h"
using namespace std;
#define int long long

int n, q;
int a[200005];
struct node{
int val;
int lazy;
} tree[800005];
void push(int k)
{
int t = tree[k].lazy;
tree[k * 2].val += t, tree[k * 2].lazy += t;
tree[k * 2 + 1].val += t, tree[k * 2 + 1].lazy += t;
tree[k].lazy = 0;
}
void update(int k, int a, int b, int u, int l, int r)
{
if (a > r || b < l) return;
if (a <= l && b >= r){
tree[k].val += u, tree[k].lazy += u;
return;
}
push(k);
int m = (l + r) / 2;
update(k * 2, a, b, u, l, m);
update(k * 2 + 1, a, b, u, m + 1, r);
tree[k].val = tree[k * 2].val + tree[k * 2 + 1].val;
}
int get(int k, int i, int l, int r)
{
if (i < l || i > r) return 0;
if (l == r) return tree[k].val;
push(k);
int m = (l + r) / 2;
return get(k * 2, i, l, m) + get(k * 2 + 1, i, m + 1, r);
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin >> n >> q;
for (int i = 0; i < n; i++) cin >> a[i];
while (q--){
int c; cin >> c;
if (c == 1){
int x, y, u; cin >> x >> y >> u;
update(1, x - 1, y - 1, u, 0, n - 1);
}
else{
int k; cin >> k;
cout << a[k - 1] + get(1, k - 1, 0, n - 1) << "\n";
}
}
return 0;
}

Forest Queries

Solution :

Sử dụng 1 mảng prefixsum 2 chiều với dp[i][j] là tổng các cây trong khoảng từ 0, 0 đến I, j
Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

signed main()
{
cin.tie(0);cout.tie(0);ios::sync_with_stdio(false);
int n, q; cin >> n >> q;
char a[1005][1005];
int dp[1005][1005] = {};
for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++){
cin >> a[i][j];
dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1] + (a[i]
[j] == '*');
}
while (q--){
int x, y, u, v; cin >> x >> y >> u >> v;
cout << dp[u][v] - dp[u][y - 1] - dp[x - 1][v] + dp[x - 1][y - 1]
<< "\n";
}
return 0;
}

Hotel Queries

Solution :

Sử dụng segment tree với mỗi node của cây lưu các giá trị số phòng tối đa trong đoạn và vị trí của
khách sạn tương ứng có cùng giá trị tối đa

Code :
#include "bits/stdc++.h"
using namespace std;

#define int long long

int n, q;
int a[200005];
struct node{
int val, id;
} tree[800005];
void build(int k, int l, int r)
{
if (l == r){
tree[k].val = a[l], tree[k].id = l;
return;
}
int m = (l + r) / 2;
build(k * 2, l, m);
build(k * 2 + 1, m + 1, r);
if (tree[k * 2].val >= tree[k * 2 + 1].val) tree[k] = tree[k * 2];
else tree[k] = tree[k * 2 + 1];
}
int get(int k, int x, int l, int r)
{
if (l > r || tree[k].val < x) return -1;
if (l == r && tree[k].val >= x) return tree[k].id;
int m = (l + r) / 2;
if (tree[k * 2].val >= x) return get(k * 2, x, l, m);
else if (tree[k * 2 + 1].val >= x) return get(k * 2 + 1, x, m + 1, r);
}
void update(int k, int u, int v, int l, int r)
{
if (u < l || u > r) return;
if (l == r){
tree[k].val -= v;
return;
}
int m = (l + r) / 2;
update(k * 2, u, v, l, m);
update(k * 2 + 1, u, v, m + 1, r);
if (tree[k * 2].val >= tree[k * 2 + 1].val) tree[k] = tree[k * 2];
else tree[k] = tree[k * 2 + 1];
}
signed main()
{

ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin >> n >> q;
for (int i = 0; i < n; i++) cin >> a[i];
build(1, 0, n - 1);
while (q--){
int b; cin >> b;
int t = get(1, b, 0, n - 1);
if (t == -1){cout << "0 "; continue;}
update(1, t, b, 0, n - 1);
cout << t + 1 << " ";
}
return 0;
}

List Removals

Solution:

Sử dụng ordered_set để tối ưu hóa các thao tác

Code:
#include "bits/stdc++.h"
using namespace std;

#include "ext/pb_ds/assoc_container.hpp"
#include "ext/pb_ds/tree_policy.hpp"
using namespace __gnu_pbds;

#define int long long

using ordered_set = tree<int, null_type, less<int>, rb_tree_tag,


tree_order_statistics_node_update>;

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n; cin >> n;
int a[n], b[n];
ordered_set s;
for (int i = 0; i < n; i++) cin >> a[i], s.insert(i);
for (int i = 0; i < n; i++) cin >> b[i];
for (int i = 0; i < n; i++){
auto it = s.find_by_order(b[i] - 1);
cout << a[*it] << " ";
s.erase(it);
}
return 0;
}

Salary Queries

Solution:

Sử dụng ordered_set để tối ưu hóa các thao tác

Nếu cần thay đổi mức lương của nhân viên thì xóa mức lương cũ của nhân viên đó trong
ordered_set đi sau đó thêm mức lương mới cho vào ordered_set

Nếu cần biết số lượng nhân viên có mức lương từ a đến b thì sử dụng hàm order_of_key() của
ordered_set

Code:
#include "bits/stdc++.h"
#include "ext/pb_ds/assoc_container.hpp"
#include "ext/pb_ds/tree_policy.hpp"
using namespace std;
using namespace __gnu_pbds;

#define int long long

template<typename T>
using ordered_set = tree<T, null_type, less<T>, rb_tree_tag,
tree_order_statistics_node_update>;
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n, q; cin >> n >> q;
int a[n];
ordered_set<pair<int, int>> s;
for (int i = 0; i < n; i++) cin >> a[i], s.insert({a[i], i});
while (q--){
char c; int i, j; cin >> c >> i >> j;
if (c == '!'){
int t = s.order_of_key({a[i - 1], i - 1});
s.erase(s.find_by_order(t));
a[i - 1] = j, s.insert({j, i - 1});
}
else cout << s.order_of_key({j + 1, 0}) - s.order_of_key({i, 0}) <<
"\n";
}
return 0;
}

Tree Algorithms
Subordinates

Solution:

Duyệt qua các là của cây, và sử dụng quy hoạch động để tính số lượng cấp dưới của đỉnh đang duyệt

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

int n;
vector<int> a[200005];
int s[200005] = {};
void dfs(int u, int p)
{
s[u] = 1;
for (int v : a[u]){
if (v == p) continue;
dfs(v, u);
s[u] += s[v];
}
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin >> n;
for (int i = 2, x; i <= n; i++){
cin >> x;
a[x].push_back(i);
}
dfs(1, 0);
for (int i = 1; i <= n; i++) cout << s[i] - 1 << " ";
return 0;
}

Tree Diameter

Solution:

Duyệt dfs lần thứ nhất để tìm đỉnh xa nhất trong 1 nhánh của cây

Sau đó chạy dfs lần thứ 2 ngược lại để tìm đỉnh xa nhất của cây trong các nhánh khác

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

int n, m = 0, node;
vector<int> a[200005];
void dfs(int u, int p, int cnt)
{
if (cnt > m) m = cnt, node = u;
for (int v : a[u]){
if (v == p) continue;
dfs(v, u, cnt + 1);
}
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin >> n;
for (int i = 0, x, y; i < n - 1; i++){
cin >> x >> y;
a[x].push_back(y);
a[y].push_back(x);
}
dfs(1, 0, 0);
dfs(node, 0, 0);
cout << m;
return 0;
}

Mathematics

Exponentiation

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

const int M = 1e9 + 7;


int power(int a, int b)
{
if (b == 0) return 1;
if (b == 1) return a % M;
int t = power(a, b / 2);
t = (t * t) % M;
if (b % 2 == 0) return t;
return ((a % M) * (t % M)) % M;
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n; cin >> n;
while (n--){
int a, b; cin >> a >> b;
cout << power(a, b) << "\n";
}
return 0;
}

Exponentiation II

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long


int M = 1e9 + 7;
int power(int a, int b)
{
if (b == 0) return 1;
if (b == 1) return a % M;
int t = power(a, b / 2);
t = (t * t) % M;
if (b % 2 == 0) return t % M;
return ((a % M) * (t % M)) % M;
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n; cin >> n;
while (n--){
int a, b, c; cin >> a >> b >> c;
M--;
int bc = power(b, c);
M++;
cout << power(a, bc) % M << "\n";
}
return 0;
}

Counting Divisors

Solution:

Tạo 1 map để lưu số lượng các ước của các số

Chạy 2 vòng lặp, ở vòng lặp thứ 2 sẽ có bước nhảy của số vòng lặp thứ nhất để các số trong vòng lặp
này chia hết cho số trong vòng lặp thứ nhất, đếm các số đó và lưu vào map

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int t; cin >> t;
int m[1000005] = {};
for (int i = 1; i <= 1000000; i++){
for (int j = i; j <= 1000000; j += i) m[j]++;
}
while (t--){
int n; cin >> n;
cout << m[n] << "\n";
}
return 0;
}

Common Divisors

Solution:
Đếm số lần xuất hiện của các phần tử mảng a và lưu vào 1 mảng

Chạy vòng lặp từ 10^6 trở về 1, với các số trong vòng lặp là ước chung lớn nhất

Chạy thêm 1 vòng lặp nữa để kiểm tra xem có bao nhiêu số có ước chung lớn nhất là số trên

Nếu có trên 2 số thì in kết quả

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n; cin >> n;
int a[n], cnt[1000005] = {};
for (int i = 0; i < n; i++) cin >> a[i], cnt[a[i]]++;
for (int gcd = 1e6; gcd > 0; gcd--){
int d = 0;
for (int i = gcd; i <= 1e6; i += gcd) d += cnt[i];
if (d >= 2){cout << gcd; return 0;}
}
return 0;
}

Fibonacci Numbers

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

const int M = 1e9 + 7;


map<int, int> F;
int f(int n)
{
if (F.count(n)) return F[n];
int k = n / 2;
if (n % 2 == 0) return F[n] = (f(k) * f(k) + f(k - 1) * f(k - 1)) % M;
return F[n] = (f(k) * f(k + 1) + f(k - 1) * f(k)) % M;
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n; cin >> n;
F[0] = F[1] = 1;
cout << (n == 0 ? 0 : f(n - 1));
return 0;
}

Advanced Techniques

Meet in the Middle


Solution:

Sử dụng phương pháp phân tập để chạy quay lui với độ phức tạp nhỏ hơn

Code:
#include "bits/stdc++.h"
using namespace std;

#define int long long

int n, x, res = 0;
int a[40];
unordered_map<int, int> m;
void ql1(int i, int s, bool check)
{
if (check) m[s]++;
if (i < n / 2){
ql1(i + 1, s, false);
ql1(i + 1, s + a[i], true);
}
}
void ql2(int i, int s, bool check)
{
if (check) res += m[x - s];
if (i < n){
ql2(i + 1, s, false);
ql2(i + 1, s + a[i], true);
}
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin >> n >> x;
for (int i = 0; i < n; i++) cin >> a[i];
m[0] = 1;
ql1(0, 0, false);
ql2(n / 2, 0, false);
res += m[0] + m[x];
cout << res - 1;
return 0;
}

Hamming Distance

Solution:

Thay đổi các số nhị phân thành số thập phân và lưu vào 1 mảng

Dùng 2 vòng lặp for, sử dụng phép toán xor và __builtin_popcount(tính tổng các bit) để tìm
hamming distance của 2 số

Code:
#include "bits/stdc++.h"

using namespace std;

#define int long long


signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n, k; cin >> n >> k;
string s[n];
vector<int> v;
for (int i = 0; i < n; i++) cin >> s[i], v.push_back(stoll(s[i], 0,
2));
int res = INT_MAX;
for (int i = 0; i < n; i++){
for (int j = i + 1; j < n; j++){
int t = v[i] ^ v[j];
res = min((int) __builtin_popcount(t), res);
}
}
cout << res;
return 0;
}

You might also like