# Editorial for Pre Tuyển Sinh 1 [A1 - Robot di chuyển](https://codeforces.com/gym/522403/problem/A1) Ta chỉ cần khởi tạo $x = 0, y = 0$ và các theo tác sau: * $L : x = x - a$ * $R : x = x + a$ * $U : y = y + a$ * $D : y = y - a$ ``` #include <bits/stdc++.h> using namespace std; long long x = 0, y = 0; signed main() { ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL); freopen("TRAVEL.INP","r",stdin); freopen("TRAVEL.OUT","w",stdout); int t; cin >> t; while (t--) { char c; long long a; cin >> c >> a; if (c == 'L') x = x - a; if (c == 'R') x = x + a; if (c == 'U') y = y + a; if (c == 'D') y = y - a; } cout << x << ' ' << y; return 0; } ``` [A2 - Bật đèn](https://codeforces.com/gym/522403/problem/A2) Với subtask đầu tiên có $n,q \le 10^3$ ta có thể dùng brute-force để giải quyết bài toán với độ phức tạp thời gian $O(2q(r - l + 1))$ $=>$ $O(nq)$ ``` #include <bits/stdc++.h> using namespace std; const int maxN = 1e6 + 17; int n,q; int a[maxN]; signed main() { ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL); freopen("LIGHTS.INP","r",stdin); freopen("LIGHTS.OUT","w",stdout); cin >> n >> q; for (int i = 1; i <= n; ++i) cin >> a[i]; while (q--) { int l,r; cin >> l >> r; int res = 0; //Xét 0 1 0 1 ... int curr = 0, ans = 0; for (int i = l; i <= r; ++i) { if (curr == a[i]) ++ans; curr = !curr; } res = ans; //Xét 1 0 1 0 ... curr = 1, ans = 0; for (int i = l; i <= r; ++i) { if (curr == a[i]) ++ans; curr = !curr; } cout << min(res,ans) << endl; } return 0; } ``` Đối với subtask 2 thì không thể dùng thuật với độ phức tạp $O(nq)$ nên cần phải có 1 thuật khác để tối ưu hóa thuật toán. 1 trong những cách tối ưu là dùng prefix sum. Ta có thể tính toán trước những bóng đèn nào thỏa mãn trong 2 trường hợp 1010 và 0101. Và khi xử lý mỗi truy vấn ta chỉ cần thực hiện việc đó trong $O(1)$. Vậy tổng thể của thuật toán này là $O(n + q)$. ``` #include <bits/stdc++.h> using namespace std; const int maxN = 1e6 + 17; int n,q; int a[maxN], odd[maxN], even[maxN]; signed main() { ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL); freopen("LIGHTS.INP","r",stdin); freopen("LIGHTS.OUT","w",stdout); cin >> n >> q; for (int i = 1; i <= n; ++i) cin >> a[i]; //Even: xét 0101 int curr = 0; for (int i = 1; i <= n; ++i) { if (curr == a[i]) even[i] = 1 + even[i - 1]; else even[i] = even[i - 1]; curr = !curr; //Đổi 1 thành 0 và ngược lại } //Odd: xét 1010 curr = 1; for (int i = 1; i <= n; ++i) { if (curr == a[i]) odd[i] = 1 + odd[i - 1]; else odd[i] = odd[i - 1]; curr = !curr; //Đổi 1 thành 0 và ngược lại } while (q--) { int l,r; cin >> l >> r; cout << min(odd[r] - odd[l - 1], even[r] - even[l - 1]) << endl; } return 0; } ``` [A3 - Số thứ tự đội hình](https://codeforces.com/gym/522403/problem/A3) Với subtask 1: $n \le 10$ ta có thể dùng brute-force để tìm số thứ tự của hoán vị ấy. ``` #include <bits/stdc++.h> using namespace std; const int maxN = 5 * 1e5 + 17; int n; int a[maxN]; long long ans = 0; signed main() { ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL); freopen("PERMUTATION.INP","r",stdin); freopen("PERMUTATION.OUT","w",stdout); cin >> n; for (int i = 1; i <= n; ++i) cin >> a[i]; do ++ans; while (prev_permutation(a + 1, a + 1 + n)); cout << ans; return 0; } ``` Với subtask 2,3: Cho ví dụ $A = [3,4,1,2]$ Đầu tiên ta cho $B = [1,2,3,4]$. Để $a[i] = 3$ trong lần đầu tiên thì cần bao nhiêu hoán vị để đạt mục tiêu? **Cách 1: Thủ công** Ta brute-force từ từ sẽ ra kết quả: 1. $B = [1,2,3,4]$ 2. $B = [1,2,4,3]$ 3. $B = [1,3,2,4]$ 4. $B = [1,3,4,2]$ 5. $B = [1,4,2,3]$ 6. $B = [1,4,3,2]$ 7. $B = [2,1,3,4]$ 8. $B = [2,1,4,3]$ 9. $B = [2,3,1,4]$ 10. $B = [2,3,4,1]$ 11. $B = [2,4,1,3]$ 12. $B = [2,4,3,1]$ 13. $B = [3,1,2,4]$ 14. $B = [3,1,4,2]$ 15. $B = [3,2,1,4]$ 16. $B = [3,2,4,1]$ 17. $B = [3,4,1,2]$ Vậy $ans = 17$ Cách 2: Ta cần chú ý hoán vị thứ 1 và hoán vị thứ 13. Tại sao lại là hoán vị thứ 13? Vì nó có $a[1] = 3$ ngay trong lần đầu tiên. Để ý thêm 1 điều nữa là khoảng cách giữa 2 hoán vị này là 12. Nếu biến đổi một chút thí $12 = 2.3!$ ($3!$ là công thức tính hoán vị 3 phần tử, $2$ là số lượng phần tử nhỏ hơn $a[1]$ và nằm đằng sau vị trí 1. Quá trình thuật toán như sau: $result = 0$ $factorial = \{0!,1!,2!,3!...,n!\}$ * Xét $i = 1$: $cnt = 0$ (số lượng phần tử nhỏ hơn $a[i]$ và nằm đằng sau vị trí i) $=> cnt = 2$ $result = result + factorial[n - 1]$ * cnt $result = 0 + 3!*2 = 12$ * Xét $i = 2$: $cnt = 0$ $=> cnt = 2$ $result = result + factorial[n - 2]$ * cnt $result = 12 + 2!*2 = 16$ * Xét $i = 3$: $cnt = 0$ $=> cnt = 0$ $result = result + factorial[n - 3]$ * cnt $result = 16 + 1!*0 = 16$ * Xét $i = 4$: $cnt = 0$ $=> cnt = 4$ $result = result + factorial[n - 4]$ * cnt $result = 16 + 0!*0 = 16$ Cuối cùng in ra kết quả là $result + 1$. *Giải thích tại $i = 2$:* Mặc dù có 3 số nhỏ hơn 4 nhưng số 3 nó đứng trước số 4 nên $cnt = 2$. Vì sao lại thế? Do ta không thể có hoán vị như thế này: $[3,1,2,4]$ ... $[3,2,1,4]$ ... ~~[3,3,1,4]~~ ... $[3,4,1,2]$ $[3,3,1,4]$ không hợp lệ vì xuất hiện 2 số 3. **Tổng thể lại kết quả của bài toán là:** $result = 1 + \sum\limits_{i = 1}^n (factorial[n - i] * query(1,a[i] - 1))$ Với $query(1, a[i] - 1)$ là đếm số lượng các phần tử sau vị trí $i$ thỏa $(1 \le x \le a[i] - 1)$ Với độ phức tạp $O(n^2)$ thì không mấy khó khăn để full subtask 2 và 3(nhớ mod nữa đấy). ``` #include <bits/stdc++.h> using namespace std; const int maxN = 5 * 1e5 + 17; const long long mod = 1e9 + 7; int n; long long a[maxN], factorial[maxN]; long long result = 0; signed main() { ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL); freopen("PERMUTATION.INP","r",stdin); freopen("PERMUTATION.OUT","w",stdout); cin >> n; for (int i = 1; i <= n; ++i) cin >> a[i]; //Khởi tạo mảng factorial factorial[0] = 1; for (int i = 1; i <= n; ++i) factorial[i] = (i * factorial[i - 1]) % mod; //Process for (int i = 1; i <= n; ++i) { //Đếm cnt long long cnt = 0; for (int j = i + 1; j <= n; ++j) if (a[j] < a[i]) ++cnt; result = (result + factorial[n - i] * cnt) % mod; } cout << (result + 1) % mod; return 0; } ``` Với subtask 4(chỉ để giải trí), ta có thể tối ưu việc tìm cnt thay vì trong $O(n)$ thành $O(logn)$ bằng [Fenwick tree](https://vnoi.info/wiki/algo/data-structures/fenwick.md). Khi đó, thuật toán giảm còn $O(n.logn)$.