Try   HackMD

Đề TS10 - BRVT - 2024: Editorial

Thông tin

Sau đây là lời giải của Kỳ thi Tuyển sinh 10 trường THPT chuyên Lê Quý Đôn tỉnh Bà Rịa - Vũng Tàu năm học 2024 - 2025.

Bạn đọc có thể nộp và chấm bài (test tự sinh) TẠI ĐÂY.

Chuẩn bị bởi team Logica, thuộc khuôn khổ dự án The Algitect (ALGI Project)

Bài 1

Tóm tắt đề bài

Cho số nguyên dương

N.

Yêu cầu: Hãy đếm số lượng các ước số dương của

N.

Dữ liệu đảm bảo:

10N1012.

Ràng buộc:

  • 50%
    số điểm tương ứng với
    1N106
  • 50%
    số điểm không có ràng buộc gì thêm.

Subtask 1

Duyệt

i từ
1
đến
n
và đếm các số
i
n
chia hết cho
i
.

Tức là

nmodi=0.

Độ phức tạp thời gian:

O(n).

Code
#include <bits/stdc++.h>
using namespace std;

long long n, kq;

int main() {
    cin >> n;

    for (long long i = 1; i <= n ;++i) {
        if (n % i == 0) {
            kq++;
        }
    }
    
    cout << kq;
    
    return 0;
}

Subtask 2

Nhận xét

Giả sử

n có một ước là
x
,
y=nx
cũng là một ước của
n
.

Quan sát thấy, luôn luôn có ít nhất một trong hai số nhỏ hơn hoặc bằng

n

Do đó, ta duyệt

i từ
1
đến
n
, nếu tìm thấy số
i
nmodi=0
, ngoài đếm ước
i
, ta đếm thêm cả ước
ni
của
n
.

Lưu ý: Trong trường hợp

i×i=n, số
i
có thể bị đếm
2
lần.

Độ phức tạp thời gian:

O(n).

Code
#include <bits/stdc++.h>
using namespace std;

long long n, kq;

int main() {
    cin >> n;

    //i <= sqrt(n) tương đương i*i <= n
    for (long long i = 1; i*i <= n; ++i) {
        if (n % i == 0) {
            kq += 2;
            if (i*i == n) {
                kq--;
            }
        }
    }

    cout << kq;
    
    return 0;
}

Bài 2

Tóm tắt đề bài

Cho một số nguyên dương

N có số lượng chữ số không vượt quá
105
.

Yêu cầu: Hoán vị các chữ số của

N, sao cho sau khi hoán vị ta thu được một số nguyên dương lớn nhất là bội của số
30
.

Ràng buộc:

  • 50%
    số điểm tương ứng với số lượng chữ số của
    N
    không vượt quá
    10
  • 50%
    số điểm không có ràng buộc gì thêm.

Về bội số của 30

Một số chia hết cho

30 khi và chỉ khỉ số đó thỏa mãn:

  • Số đó chia hết cho
    3
    , nghĩa là tổng các chữ số chia hết cho
    3
    .
  • Số đó chia hết cho
    10
    , nghĩa là có số
    0
    ở cuối
    .

Subtask 1

Ta thử tất cả các cách hoán vị các chữ số của

N và tìm số lớn nhất thỏa mãn tất cả điều kiện ở trên.

Để dễ thao tác, ta sử dụng hàm next_permutation() có sẵn trong thư viện STL của C++.

Độ phức tạp thời gian:

O(n!)

Subtask 2

Ta sắp xếp lại các chữ số theo thứ tự giảm dần để số đạt giá trị lớn nhất.

Trùng hợp, khi đó số

0 (nếu có) của
N
cũng sẽ được dồn về cuối số, giúp ta thỏa mãn điều kiện thứ
2
.

Khi ta sắp xếp lại như vậy, tổng các chữ số không bị thay đổi, nên ta kiểm tra xem tổng này có chia hết cho

3 hay không.

Tip

Để dễ thao tác, ta nên sử dụng kiểu dữ liệu string để lưu số

N.

Độ phức tạp thời gian:

O(nlogn) với
n
là số lượng chữ số của
N
.

Code
#include <bits/stdc++.h>
using namespace std;

int sumDig(string s) {
	int ret = 0;
	for (char x : s) {
		ret += (x - '0');
    }

	return ret;
}

int main() {
	string s;
	cin >> s;
	sort(all(s), greater<char>());

	if (s.back() != '0' || sumDig(s) % 3) cout << -1;
	else cout << s;

	return 0;
}

Bài 3

Tóm tắt đề bài

Cho dãy

A gồm
N
số nguyên dương. Bằng cách ghi dãy
A
lặp lại vô hạn lần ta thu được dãy
B
.

Yêu cầu: Cho số nguyên dương

K,P. Tính tổng
K
phần tử liên tiếp trong dãy
B
bắt đầu từ phần tử có chỉ số
P
.

Dữ liệu đảm bảo:

  • 1N105
    ;
  • 1K,P109
    ;
  • 1Ai103
    .

Ràng buộc:

  • 50%
    số điểm tương ứng với
    1K104,1P105
  • 50%
    số điểm không có ràng buộc gì thêm.

Subtask 1

Thực hiện mô phỏng theo đề bài, ta duy trì một biến vị trí để di chuyển tăng dần, nếu vị trí lớn hơn

n, ta cập nhật lại biến về
1
.

Mẹo

Sử dụng phép toán

mod để cập nhật lại vị trí.

Độ phức tạp thời gian:

O(P).

Code
#include <bits/stdc++.h>
using namespace std;

long long n, kq, l, r, a[1000009], k ,p;

int main() {
    cin >> n >> k >> p;
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
    }
    
    a[0] = a[n];
    
    for (int i = p; i <= k + p - 1; ++i) {
        kq += a[i % n];
    }
    
    cout << kq;
    
    return 0;
}

Subtask 2

Nhận xét

Dãy

B là dãy
A
lặp đi lặp lại vô tận.

Do đó, tổng của

K phần tử liên tiếp mà đề bài yêu cầu ta tính cũng được tạo thành từ nhiều tổng của cả dãy
A
cộng lại và một ít số bị lẻ ra.

Ta chỉ cần duyệt để tính các số bị lẻ ra, còn nhóm tổng của

A có thể tính nhanh.

Độ phức tạp thời gian:

O(n).

Code
#include <bits/stdc++.h>
using namespace std;

const int N = 1e5;
long long n, k, p, sum, A[N+1];

int main() {
	cin >> n >> k >> p;
	for (int i = 1; i <= n; i++) {
		cin >> A[i];
		sum += A[i];
	}
    
    A[0] = A[n];

	p %= n;
	if (!p) p = n;
    
    //Số lượng nhóm có tổng = A
	long long cycle = k / n;
    
    //Phần bị lẻ ra
	long long rem = k % n;

	long long res = cycle * sum;
	for (int i = p; i <= p + rem - 1; i++) {
		res += A[i % n];
	}

	cout << res;

	return 0;
}

Bài 4

Tóm tắt đề bài

Cho dãy

B gồm các số nguyên
B1,B2,B3,,BN
được gọi là dãy lõm nếu tồn tại chỉ số
i
(1<i<N)
sao cho
B1>B2>>Bi<Bi+1<<BN
.

Yêu cầu: Cho trước dãy

A gồm
N
số nguyên dương
A1,A2,,AN
. Hãy tìm dãy con lõm có độ dài lớn nhất.

Dữ liệu đảm bảo:

2<N105
1Ai109
.

Ràng buộc:

  • 20%
    số điểm tương ứng với
    2N20
    .
  • 30%
    số điểm tương ứng với
    20<N5000
    .
  • 50%
    số điểm không có ràng buộc gì thêm.

Subtask 1

Sinh tất cả dãy con của

A, kiểm tra tính hợp lệ, và lấy độ dài dãy con dài nhất.

Độ phức tạp thời gian:

O(2n).

Subtask 2

Nhận xét

Một dãy con lõm là hợp bởi một dãy con giảm và một dãy con tăng (mỗi dãy gồm ít nhất hai phần tử).

Áp dụng quy hoạch động cơ bản.

  • Định nghĩa:
    • Fi
      là dãy con giảm dài nhất kết thúc tại
      i
      .
    • Gi
      là dãy con tăng dài nhất bắt đầu tại
      i
      .
  • Khởi gán:
    Fi=Gi=1i
    (dãy con một phần tử).
  • Công thức:
    • Fi=max(Fj+1)
      với
      j<i
      Aj>Ai
      (thêm
      Ai
      vào một dãy kết thúc ở
      Aj
      ).
    • Gi=max(Fj+1)
      với
      j>i
      Aj>Ai
      (thêm
      Ai
      vào một dãy kết thúc ở
      Aj
      ).

Khi đó, ở mỗi vị trí

i từ
1
đến
n
, nếu
Fi2
Gi2
thì có một dãy con lõm nhận
i
làm điểm thấp nhất (điểm lõm) có độ dài là:
Fi+Gi1

Độ phức tạp thời gian:

O(n2).

Code
#include <bits/stdc++.h>
using namespace std;

long long n, a[1000009], F[1000009], G[1000009], kq;

int main() {
    cin >> n;
    
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    
    for (int i = 1; i <= n; i++) {
        F[i] = 1;
        for (j = 1; j <= i-1; j++) {
            if (a[j] > a[i]) {
                F[i] = max(F[i], F[j]+1);
            }
        }
    }
        
    for (int i = n; i >= 1; i--) {
        G[i] = 1;
        for (j = n; j >= i+1; j--) {
            if (a[j] > a[i]) {
               G[i] = max(G[i], G[j]+1);
            }
        }
    }
    
    for (int i = 1; i <= n; i++) {
        kq = max(kq, F[i] + G[i] - 1);
    }

    cout << kq;
    
    return 0;
}

Subtask 3

Tư tưởng kế thừa từ subtask trước, tuy nhiên cần cải tính việc tính

F
G
.

Bạn đọc tham khảo bài viết về giải thuật dãy con tăng dài nhất nâng cao sau đây.

Độ phức tạp thời gian:

O(nlogn).

Code
#include <bits/stdc++.h>
using namespace std;

const int N = 1e5;
int n, A[N+1], F[N+1], G[N+1];
vector<int> V;

int findPos(int x) {
    int l = 0, r = V.size() - 1, ret = 0;
    while (l <= r) {
        int mid = (l + r) >> 1;
        if (V[mid] <= x) {
            ret = mid;
            r = mid - 1;
        } else
            l = mid + 1;
    }

    return ret;
}

void initLtoR() {
    V.clear();
    for (int i = 1; i <= n; i++) {
        if (V.empty() || V.back() > A[i]) {
            V.pb(A[i]);
            F[i] = V.size();
        } else {
            int pos = findPos(A[i]);
            V[pos] = A[i];
            F[i] = pos + 1;
        }
    }
}

void initRtoL() {
    V.clear();
    for (int i = n; i >= 1; i--) {
        if (V.empty() || V.back() > A[i]) {
            V.pb(A[i]);
            G[i] = V.size();
        } else {
            int pos = findPos(A[i]);
            V[pos] = A[i];
            G[i] = pos + 1;
        }
    }
}

int main() {
    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> A[i];

    initLtoR();
    initRtoL();

    int res = 0;
    for (int i = 1; i <= n; i++) {
        if (F[i] >= 2 && G[i] >= 2) {
            res = max(res, F[i] + G[i] - 1);
        }
    }

    cout << res;

    return 0;
}