## Solution CHV training [1]
https://oj.vnoi.info/contest/chvgl_chv
# 1. Sum T.
___
> ###### Tags: Two pointers, Binary Search.
> https://oj.vnoi.info/problem/chvgl_sumt
___
### **Nhận xét** :
+ Ta chỉ xét trên một đoạn liên tục trong dãy, cũng như các phần tử trong mảng là dương.
+ Ta cho thể nhận thấy được rằng nếu có đoạn con độ dài $x$ thỏa mãn điều kiện thì các đoạn con có độ dài $1, 2..\;, x - 1$ cũng thỏa mãn điều kiện.
=> Từ đó ta có thể áp dụng **Two pointers, Binary Search**.
### **Giải theo Binary Search :**
* Như nhận xét trên ta sẽ tìm kiếm kết quả trên đoạn $[l, \; r]$, nếu giá trị $mid$ đang kiểm tra thỏa mãn ta sẽ kiểm tra trên đoạn $[mid + 1, \; r]$, ngược lại $[l, \; mid - 1]$. Giá trị $x$ chỉ thỏa mãn khi tồn tại ít nhất một đoạn liên tiếp độ dài $k$ có tổng nhỏ hơn $t$.
**Code**
> **Time : $O(n * log\;n$)**
> **Tags : Binary Search, Prefix sum**
```Cpp=
#include <bits/stdc++.h>
using namespace std;
#define FOR(i, l, r) for (int i = (l); i <= (r); ++i)
const int INF = 1e9;
const int N = 1e5 + 5;
int n, s, pre[N];
bool check(int len) {
if (len > n) return 0;
FOR(i, 1, n - len + 1)
if (pre[i + len - 1] - pre[i - 1] <= s) return 1;
return 0;
}
void test_case() {
cin >> n >> s;
FOR(i, 1, n) cin >> pre[i], pre[i] += pre[i-1];
if (pre[n] <= s) {
cout << n;
return;
}
int l = 1, r = N;
while (l <= r) {
int mid = (l + r) >> 1;
if (check(mid)) l = mid + 1;
else r = mid - 1;
}
cout << l - 1;
}
int main() {
ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int TC = 1;
while (TC--) test_case();
return 0;
}
```
### **Giải theo Two pointers:**
* Có thể tham khảo qua : [https://vnoi.info/wiki/algo/basic/two-pointers.md](https://vnoi.info/wiki/algo/basic/two-pointers.md)
**Code :**
> **Time : $O(n$)**
> **Tags : Two pointers**
```cpp=
#include<bits/stdc++.h>
using namespace std;
#define all(x) begin(x), end(x)
#define sz(x) (int)(x).size()
#define MASK(i) (1ll << (i))
#define BIT(x, i) (((x) >> (i)) & 1ll)
#define builtin_popcount __builtin_popcountll
#define Times cerr <<"\nTime: "<<clock()/(double)1000<<" sec"
// cout << setprecision(0) << fixed << ans;
const int N = (int)1e5 + 5;
const int Sz = (int)1e6 + 5;
const int INF = (int)1e9;
const int MOD = (int)1e9 + 7;
/***------------------------- END ---------------------------***/
int n, k, a[N];
#define TASK ""
int main() {
ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#ifndef ONLINE_JUDGE
// freopen(TASK".inp", "r", stdin);
// freopen(TASK".out", "w", stdout);
#endif
cin >> n >> k;
for (int i = 1; i <= n; i++) cin >> a[i];
int res = 0, l = 1, sum = 0;
for (int r = 1; r <= n; r++) {
sum += a[r];
while (sum > k) sum -= a[l++];
res = max(res, r - l + 1);
}
cout << res;
return 0;
}
// DP_196
```
# 2. K - Sum.
---
[https://oj.vnoi.info/problem/chvgl_kth](https://oj.vnoi.info/problem/chvgl_kth)
##### Tags : Sort, CTDL C++.
---
### Subtask 1 ($n <= 10 ^ 5$) :
* Ta có thể sort mảng lại, hoặc dùng các CTDL để chọn ra $k$ phần tử đầu tiên trong mảng với độ phức tạp là **$O(n * log \; n)$**.
### Subtask 2 ($n <= 10 ^ 6$) :
* Khi bài toán chỉ còn $0.15s$ thì cách giải của **subtask 1** không còn phù hợp nữa. Thì với trường hợp này đối với $C++$ thì ta sẽ dùng lệnh **nth_element**. Cách dùng như sau :
* **Cú pháp :** (tên mảng + chỉ số đầu, tên mảng + giá trị $k$, tên mảng + chỉ số cuối).
* **Ý nghĩa :** Sau khi thực hiện lệnh này $k$ phần tử bé nhất của mảng sẽ được đẩy lên $k$ vị trí đầu tiên của mảng.
* **Lưu ý** : Sau khi lệnh được thực thi thì $k$ phần tử đầu tiên của mảng là $k$ giá trị bé nhất, nhưng không có nghĩa $k$ phần tử này được sắp xếp tăng dần.
* **Độ phức tạp** : $O(n)$.
**Code :**
```cpp=
#include<bits/stdc++.h>
using namespace std;
#define all(x) begin(x), end(x)
#define sz(x) (int)(x).size()
#define MASK(i) (1ll << (i))
#define BIT(x, i) (((x) >> (i)) & 1ll)
#define builtin_popcount __builtin_popcountll
#define Times cerr <<"\nTime: "<<clock()/(double)1000<<" sec"
// cout << setprecision(0) << fixed << ans;
const int N = (int)1e5 + 5;
const int Sz = (int)1e6 + 5;
const int INF = (int)1e9;
const int MOD = (int)1e9 + 7;
/***------------------------- END ---------------------------***/
int n, k, a[Sz];
#define TASK ""
int main() {
ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#ifndef ONLINE_JUDGE
// freopen(TASK".inp", "r", stdin);
// freopen(TASK".out", "w", stdout);
#endif
cin >> n >> k;
for (int i = 1; i <= n; i++) cin >> a[i];
nth_element(a + 1, a + k, a + n + 1);
int64_t res = 0;
for (int i = 1; i <= k; i++) res += a[i];
cout << res;
return 0;
}
// DP_196
```
> 10 người đọc đề hết 11 người tưởng mình sẽ AC sau 30 s :))))))
# 3. Hihi
___
[https://oj.vnoi.info/problem/chvgl_hihi](https://oj.vnoi.info/problem/chvgl_hihi)
###### Tags: Sort, Two pointers.
**Code :**
> **Time : $O(n * log \; n$)**
```Cpp==
#pragma GCC optimize("unroll-loops,02,no-stack-protector")
#include<bits/stdc++.h>
using namespace std;
#define TASK ""
#define p first
#define t second
#define all(x) begin(x), end(x)
#define sz(x) (int)(x).size()
#define MASK(i) (1ll << (i))
#define BIT(x, i) (((x) >> (i)) & 1ll)
#define builtin_popcount __builtin_popcountll
typedef pair<int, int> ii;
const int N = (int)1e5 + 5;
const int Sz = (int)1e6 + 5;
const int INF = (int)1e9;
const int MOD = (int)1e9 + 7;
void setIO() {
ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
// freopen(TASK".inp","r",stdin);
// freopen(TASK".out","w",stdout);
}
/***------------------------- END ---------------------------***/
int n, m, res = INF;
ii a[N];
unordered_map<int, int> cnt;
int main() {
setIO();
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i].p >> a[i].t;
cnt[a[i].t]++;
}
sort(a + 1, a + n + 1);
m = sz(cnt);
cnt.clear();
int l = 1;
for (int r = 1; r <= n; r++) {
cnt[a[r].t]++;
while (l <= r && cnt[a[l].t] > 1) --cnt[a[l++].t];
if (sz(cnt) == m) res = min(res, a[r].p - a[l].p);
}
cout << res;
return 0;
}
// DP_196
```
# 4. 196.
___
##### Tags : Nhân ma trận (Matrix multiplication).
[https://oj.vnoi.info/problem/chvgl_196](https://oj.vnoi.info/problem/chvgl_196)
**Tham khảo** : [https://vnoi.info/wiki/algo/trick/matrix-multiplication.md](https://vnoi.info/wiki/algo/trick/matrix-multiplication.md)
___
### Với $n <= 10 ^ 6:$
+ Các bạn có thể chạy tuyến tính để tạo ra mảng $f[i]$.
### Với $n <= 10 ^ {18}:$
+ Hiển nhiên cách làm thông thường là tính lần lượt các $f[i]$. Tuy nhiên, cách làm này hoàn toàn không hiệu quả với $n$ lên đến $n <= 10 ^ {18}$, và ta cần một cách tiếp cận khác.
+ Ta xét các lớp số :
* Lớp 1 : $f[1], \: f[2], \: f[3]$
* Lớp 2 : $f[2], \: f[3], \: f[4]$
* ...
* Lớp i : $f[i], \: f[i - 1], \: f[i - 3]$
+ Ta hình dung mỗi lớp là một ma trận $(3 × 1)$. Tiếp đó, ta sẽ biến đổi từ lớp $i - 1$ đến lớp $i$. Sau mỗi lần biến đổi như vậy, ta tính thêm được một giá trị $f[i]$. Để thực hiện phép biến đổi này, chú ý là các số ở lớp sau chỉ phụ thuộc vào lớp ngay trước nó theo các phép cộng, ta tìm được cách biến đổi bằng nhân ma trận:
+ $$
A \: ×
\begin{bmatrix}
f[i - 1] \\
f[i - 2] \\
f[i - 3]
\end{bmatrix}
=
\begin{bmatrix}
f[i] \\
f[i - 1] \\
f[i - 2]
\end{bmatrix}
$$
* Ma trận $A$ (Ma trận cơ sở - Các bạn tham khảo qua **VNOI**) ở đây sẽ là :
* $$
A =
\begin{bmatrix}
1 & 9 & 6 \\[0.3em]
1 & 0 & 0 \\[0.3em]
0 & 1 & \ 0
\end{bmatrix}
$$
### Code :
___
> Times : $O(3 ^ 3 * q * log \: n)$
___
```Cpp=
#pragma GCC optimize("unroll-loops,02,no-stack-protector")
#include<bits/stdc++.h>
using namespace std;
#define TASK ""
#define int int64_t
#define all(x) begin(x), end(x)
#define sz(x) (int)(x).size()
#define MASK(i) (1ll << (i))
#define BIT(x, i) (((x) >> (i)) & 1ll)
#define builtin_popcount __builtin_popcountll
const int N = (int)1e5 + 5;
const int Sz = (int)1e6 + 5;
const int INF = (int)1e9;
const int MOD = (int)1e9 + 7;
void setIO() {
ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
// freopen(TASK".inp","r",stdin);
// freopen(TASK".out","w",stdout);
}
/***------------------------- END ---------------------------***/
int ntest, n;
struct Matrix {
int a[4][4] = {{0, 0}, {0, 0}, {0, 0}};
Matrix operator * (const Matrix& other) const {
Matrix product;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
for (int k = 0; k < 3; k++)
product.a[i][k] = (product.a[i][k] +
a[i][j] * other.a[j][k] % MOD) % MOD;
}
}
return product;
}
};
Matrix pw(Matrix a, int k) {
Matrix res;
for (int i = 0; i < 3; i++) res.a[i][i] = 1;
while (k) {
if (k & 1) res = res * a;
k >>= 1;
a = a * a;
}
return res;
}
int32_t main() {
setIO();
cin >> ntest;
Matrix s;
s.a[1][0] = s.a[2][1] = 1;
s.a[0][2] = 6;
s.a[1][2] = 9;
s.a[2][2] = 1;
for (int k = 1; k <= ntest; k++) {
cin >> n;
if (n <= 1) cout << "1\n";
else if (n <= 2) cout << "9\n";
else if (n <= 3) cout << "6\n";
else {
Matrix tmp = pw(s, n - 3);
int res = ((tmp.a[0][2] + 9 * tmp.a[1][2] % MOD) % MOD + 6 * tmp.a[2][2] % MOD) % MOD;
cout << res << '\n';
}
}
return 0;
}
// DP_196
```
# 5. Tổng các ước.
___
[https://oj.vnoi.info/problem/chvgl_sdiv](https://oj.vnoi.info/problem/chvgl_sdiv)
___
## Các kiến thức cần có :
* Sàng nguyên tố, phân tích thừa số nguyên tố với thời gian thực hiện hợp lí.
* Sàng nguyên tố $O(n * log \: n)$, với mảng $prime[i]$ là số nguyên tố bé nhất mà $i$ chia hết cho số đó:
```cpp=
for (int i = 2; i * i < Sz; i++)
if (!prime[i])
for (int j = i * i; j < Sz; j += i)
if (!prime[j]) prime[j] = i;
for (int i = 2; i < Sz; i++) if (!prime[i]) prime[i] = i;
```
* Phân tích thừa số nguyên tố trong $O(log \: n)$ :
```cpp=
void factlog(int n) {
while (n != 1)
mark[prime[n]]++, n /= prime[n];
}
```
* Các kiến thức về toán học, phép đồng dư.
* Công thức tính tổng ước.
* Khi số $MOD$ là số nguyên tố thì ta sẽ có : $(a \: / \: b)$ % $MOD = a \: * \: pow(b, \: MOD - 2)$ % $MOD$.
## Code :
___
> Tags : Math
> Times : $O(n * log \: n)$
___
```Cpp=
#include<bits/stdc++.h>
using namespace std;
#define int int64_t
#define all(x) begin(x), end(x)
#define sz(x) (int)(x).size()
#define MASK(i) (1ll << (i))
#define BIT(x, i) (((x) >> (i)) & 1ll)
#define builtin_popcount __builtin_popcountll
#define Times cerr <<"\nTime: "<<clock()/(double)1000<<" sec"
// cout << setprecision(0) << fixed << ans;
const int N = (int)1e5 + 5;
const int Sz = (int)1e6 + 5;
const int INF = (int)1e9;
const int MOD = (int)1e9 + 7;
/***------------------------- END ---------------------------***/
int n, k, prime[Sz];
unordered_map<int, int> mark;
void factlog(int n) {
while (n != 1)
mark[prime[n]]++, n /= prime[n];
}
int pw(int x, int n) {
int res = 1;
while (n > 0) {
if (n & 1) res = res * x % MOD;
n >>= 1;
x = x * x % MOD;
}
return res;
}
#define TASK ""
int32_t main() {
ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
// #ifndef ONLINE_JUDGE
// freopen(TASK".inp", "r", stdin);
// freopen(TASK".out", "w", stdout);
// #endif
for (int i = 2; i * i < Sz; i++)
if (!prime[i])
for (int j = i * i; j < Sz; j += i)
if (!prime[j]) prime[j] = i;
for (int i = 2; i < Sz; i++) if (!prime[i]) prime[i] = i;
cin >> n;
for (int i = 1; i <= n; i++) {
int x;
cin >> x;
factlog(x);
}
cin >> k;
for (auto it : mark) mark[it.first] = it.second * k;
int res = 1;
for (auto it : mark) {
int a = (pw(it.first, it.second + 1) - 1 + MOD) % MOD;
int b = pw(it.first - 1, MOD - 2);
res = ((res * a) % MOD * b % MOD) % MOD;
}
cout << res;
return 0;
}
// DP_196
```
> Ai có thắc mắc hãy liên hệ qua : [https://www.facebook.com/profile.php?id=100040553432971](https://www.facebook.com/profile.php?id=100040553432971). Đoạn sau lười quá nên không viết nữa mong mọi người thông cảm =((
{"metaMigratedAt":"2023-06-17T04:16:58.886Z","metaMigratedFrom":"YAML","title":"CHV training [1]","breaks":"true","contributors":"[{\"id\":\"3d74aaa0-36da-4c3b-ae6c-454a8b6c8fd7\",\"add\":12521,\"del\":477}]"}