Try   HackMD

Đề THT B - BRVT - 2023: Editorial

Thông tin

Sau đây là lời giải của Kỳ thi Tin học trẻ bảng B tỉnh Bà Rịa - Vũng Tàu năm học 2022 - 2023.

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: Giá trị lớn nhất (40 điểm)

Tóm tắt đề bài

Cho hai số nguyên dương

n
m
.

Yêu cầu: Hãy tìm số nguyên dương

k lớn nhất thỏa mãn
n!
chia hết cho
mk
.

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

n1018
m106
.

Subtasks:

  • 60%
    số điểm ứng với
    n106
    .
  • 40%
    số điểm không có ràng buộc gì thêm.

Subtask 1

Kiến thức toán học cần biết

Giả sử

a=p1x1p2x2p3x3pkxk với
p1,p2,,pk
là các thừa số nguyên tố của
a
.
Khi đó, một số
b
chia hết cho
a
khi và chỉ khi với mọi thừa số nguyên tố
pixi
của
a
thì số
b
cũng nhận
pjxj
làm thừa số nguyên tố trong đó
pj=pi
xjxi
.

Ví dụ:

a=10=2151
b=200=2352

b chia hết cho
a
vì:

  • Với thừa số nguyên tố
    2
    , khi phân tích số
    a
    b
    ta được số mũ
    3>2
    .
  • Với thừa số nguyên tố
    5
    , khi phân tích số
    a
    b
    ta được số mũ
    2>1

Cách phân tích thừa số nguyên tố của một số

x trong
x

Ta phân tích thừa số nguyên tố của

k, được các thừa số
p
, gọi:

  • x
    là số mũ của
    p
    trong phân tích thừa số nguyên tố của
    k
  • y=cntp
    là số mũ của
    p
    trong phân tích thừa số nguyên tố của
    n!

Để tính

cntp, ta duyệt từng số từ
1
đến
n
và chia số đó cho
p
đến khi nào không chia được nữa thì dừng.
cntp
bằng tổng số lần chia.

Kết quả: Giá trị nhỏ nhất của

yx (
y
chia lấy nguyên cho
x
).

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

O(1+2++n)=O(i=1ni).

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

const int N = 1e6;
long long n, m, res = 1e18;

long long cnt(long long p) {
    long long ret = 0;
    for (long long i = p; i <= n; i++) {
        long long dup = i;
        while (dup % p == 0) {
            dup /= p;
            ret++;
        }
    }

    return ret;
}

int main() {
    cin >> n >> m;

    long long p = 2;
    while (p*p <= m) {
        if (m % p == 0) {
            long long pw = 0;
            while (m % p == 0) {
                m /= p;
                pw++;
            }

            int x = cnt(p);
            res = min(res, x / pw);
        }

        p++;
    }

    if (m > 1) res = min(res, cnt(m));

    cout << res;

    return 0;
}

Subtask 2

Ý tưởng kế thừa từ subtask trước, tuy nhiên cần cải tiến khâu phân tích thừa số nguyên tố của

n!

Quan sát và nhận xét

Xét

10!=123n và số nguyên tố
p=2
. Từ
1
đến
n
có:

  • 2=21
    .
  • 4=22
    .
  • 6=23
    .
  • 8=23
    .
  • 10=25

Khi đó ta nói

10! có:

  • 5
    số
    2
    bậc
    1
    2,4,6,8,10
    .
  • 2
    số
    2
    bậc
    2
    4,8
    .
  • 1
    số
    2
    bậc
    3
    8
    .

Như vậy, có thể tính nhanh số mũ

x của một số nguyên tố
p
bất kỳ trong phân tích thừa số nguyên tố của
n!
bằng cách:

  • Tính số lượng số
    p
    xuất hiện ở bậc
    1
    .
  • Tính số lượng số
    p
    xuất hiện ở bậc
    2
    .

Và tính tổng số lượng số

ptất cả các bậc.

Công thức

Số lượng thừa số nguyên tố

p bậc
k
trong phân tích thừa số nguyên tố của
n!
là:
npk

Dễ thấy, ta chỉ có thể thực hiện chia cho tối đa
plogpn
trước khi kết quả của phép tính đạt
0
.

Vậy số lần tối đa ta cần phải chia để đếm số mũ của số nguyên tố

p trong phân tích thừa số nguyên tố của
n!
logn
.

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

O(logn×k)

Code
#include <bits/stdc++.h> using namespace std; long long n, m, res = 1e18; long long cnt(long long p) { long long ret = 0, dup = n; while (dup /= p) { ret += dup; } return ret; } int main() { cin >> n >> m; int p = 2; while (p*p <= m) { if (m % p == 0) { int pw = 0; while (m % p == 0) { m /= p; pw++; } long long x = cnt(p); res = min(res, x / pw); } p++; } if (m > 1) res = min(res, cnt(m)); cout << res; return 0; }

Bài 2: Cặp số (30 điểm)

Tóm tắt đề bài

Cho số nguyên dương

n. Hai số nguyên dương
a
,
b
được gọi là cặp số may mắn nếu thỏa mãn tất cả các điều kiện sau:

  • 0<ab
  • a+b=n
  • Ước số chung lớn nhất của
    a
    b
    là lớn nhất.

Yêu cầu: Hãy tìm cặp số

(a,b) thỏa mãn tất cả các điều kiện trên. Nếu có nhiều cặp thì cho biết cặp số có giá trị
a
nhỏ nhất.

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

2n1012.

Subtasks:

  • 60%
    số điểm ứng với
    n106
    .
  • 40%
    số điểm không có ràng buộc gì thêm.

Subtask 1

Duyệt

a từ
1
đến
n2
, ta tính được
b=na
(nếu
a>n2
thì
b<a
).

Sử dụng hàm __gcd() có sẵn trong C++ STL để kiểm tra điều kiện ước số chung lớn nhất của

a
b
là lớn nhất và lấy đáp án.

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

O(nlogX) với
X=12+22++n2
.

Để tính UCLN của hai số

a,b mất độ phức tạp
logmax(a,b)
.
Với mỗi số
x
, có
x
cặp số mà trong đó
x
là số lớn hơn.

Code
#include <bits/stdc++.h> using namespace std; long long n, res, resA, resB; int main() { cin >> n; for (int a = 1; a <= n/2; a++) { long long b = n - a, t = __gcd(a, b); if (t > res) { res = t; resA = a; resB = b; } else if (t == res && resA > a) { resA = a; resB = b; } } cout << resA << " " << resB; return 0; }

Subtask 2

Giả sử ta có hai số

a,b thỏa mãn
a+b=n
d=UCLN(a,b)
.

Ta có:

  • a=d×x
    với
    xN
  • b=d×y
    với
    yN
  • a+b=d×(x+y)

Lại có:

a+b=n
d×(x+y)=n

x+y=nd

Có thể thấy, chỉ tồn tại hai số

a,b nhận ước chung lớn nhất
d
nếu
d
là ước của
n
nd2
.

Khi tìm được

d lớn nhất thỏa mãn, để
a
nhỏ nhất, ta xác định:

  • a=d
    .
  • b=d×(nd1)
    .

Giá trị

d lớn nhất ở đây cũng chính là ước lớn nhất của
n
mà bé hơn hoặc bằng
n2
. Ta duyệt qua các ước của
n
để tìm giá trị này.

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

O(n).

Code
#include <bits/stdc++.h> using namespace std; long long n, res; int main() { cin >> n; for (int i = 1; i*i <= n; i++) { if (n % i == 0) { if (i <= n/2) res = max(res, i); if ((n/i) <= n/2) res = max(res, n/i); } } cout << res << " " << res * (n / res - 1); return 0; }

Bài 3: Giá trị lớn nhất (30 điểm)

Tóm tắt đề bài

n con kiến đi theo một đoàn, con kiến thứ
i
có khối lượng
ki
và vận tốc di chuyển
vi
. Trên đường đi có một cành cây có độ dài
l
chỉ cho phép khối lượng của một nhóm kiến đi qua tối đa là
m
.

Ở mọi thời điểm, chỉ được có tối đa một nhóm kiến trên cành cây, sau khi nhóm đó đi xong thì nhóm sau mới được đi.

Vận tốc của một nhóm kiến là vật tốc của con kiến chậm nhất.

Yêu cầu: Sắp xếp các nhóm kiến sao cho thời gian qua cành cây là nhỏ nhất.

Lưu ý: Đoàn kiến bắt buộc phải đi tuần từ (không được thay đổi thứ tự của các con kiến).

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

n103,
1m,l,ki,vi100
.

Lời giải

Áp dụng quy hoạch động:

  • Định nghĩa:
    dpi
    là thời gian ngắn nhất để
    i
    con kiến đầu tiên đi qua cành cây.
  • Khởi gán:
    dp0=0
    ban đầu chưa có con kiến nào đi qua.
  • Công thức:
    dpi=min(dpj+cost(j+1,i))
    • Giải thích: Tách các con kiến
      j+1,j+2,,i
      thành một nhóm.
    • Điều kiện:
      j<i
      kj+1+kj+2++kim
      .
    • cost(j+1,i)=lmin(vj+1,vj+2,vi)
      là thời gian để nhóm kiến này đi qua cành cây.
  • Đáp án:
    dpn
    .

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

O(n2).

Code
#include <bits/stdc++.h> using namespace std; const int N = 1000; long long n, m, l, K[N+1], V[N+1]; double F[N+1]; int main() { cin >> n >> m >> l; for (int i = 1; i <= n; i++) cin >> K[i] >> V[i]; for (int i = 1; i <= n; i++) { long long sum = K[i], velo = V[i]; F[i] = F[i-1] + double(l)/velo; for (int j = 1; sum + K[i-j] <= m && j < i; j++) { sum += K[i-j]; velo = min(velo, V[i-j]); F[i] = min(F[i], F[i-j-1] + double(l)/velo); } } cout << fixed << setprecision(2) << F[n]; return 0; }