Try   HackMD

Đề TS10 - BRVT - 2015: 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 2015 - 2016.

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. Tính giá trị các biểu thức sau:
A=i=3n1iB=i=3n(i2i1i)C=i=12n+1(1)i2ii!

Tính biểu thức A

Khởi gán

A=0. Duyệt qua mọi
i[3,n]
và cộng
1i
vào A.

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

O(n)

Tính biểu thức B

Khởi gán

B=1. Duyệt qua mọi
i[3,n]
và nhân
i2i1i
vào B.

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

O(n)

Tính biểu thức C

Gọi

f(i)=2ii!. Khi này:

  • f(0)=1
  • f(i)=f(i1)2i
    .

Khởi gán

C=0.

Duyệt qua mọi

i[1,n], nếu
i
lẻ thì cộng
f(i)
vào
C
, ngược lại trừ
f(i)
vào
C
.

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

O(n)

Lưu ý: Vì bài toán liên quan tới số thực. Vậy nên cần sử dụng kiểu dữ liệu double hoặc long double để đảm bảo độ chính xác.

Độ phức tạp thời gian của cả bài toán là tổng độ phức tạp thời gian của từng yêu cầu.

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

int n;
long double A = 0, B = 1, C = 0, v = 1;

int main() {
    cin >> n;
    for (int i = 3; i <= n; i++) {
        A += (long double)1 / i;
    }
    
    for (int i = 3; i <= n; i++) {
        B *=  (long double)(i - 2) / (i - 1) - sqrtl(i);
    }
    
    for (int i = 1; i <= 2*n+1; i++) {
        v *= (long double)2 / i;
        if (i & 1) {
            C += v;
        }
        else {
            C -= v;
        }
    }
    
    cout << fixed << setprecision(4) << A << '\n' << B << '\n' << C;
    
    return 0;
}

Bài 2:

Tóm tắt đề bài

Cho 3 số nguyên dương

n,a,b.

  • Tìm bội chung nhỏ nhất của
    a
    b
    .
  • Cho biết
    n
    có số lượng ước chẵn hay lẻ.

Tìm bội chung nhỏ nhất

Tip

Bội chung nhỏ nhất của

a
b
là:

BCNN(a,b)=abUCLN(a,b)

Sử dụng hàm gcd() có sẵn trong thư viện STL của C++, dễ dàng tính được đáp án.

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

O(logmin(a,b))

Đây là độ phức tạp của thuật toán Euclid để tính UCLN.

Cho biết số có số lượng ước chẵn hay lẻ

Nhận xét

Nếu một số nguyên dương

x1 là số chính phương thì có số lượng ước lẻ, ngược lại số lượng ước là chẵn.

Chứng minh

  • Một số nguyên dương
    x1
    có thể được viết dưới dạng
    p1a1×p2a2pkak
    , với
    pi
    là ước số nguyên tố của
    x
    .
  • Số lượng ước của
    x
    được tính theo công thức
    d(x)=(a1+1)(a2+1)(ak+1)
    .
  • Nếu
    x
    là số chính phương khác
    1
    , mọi ước nguyên tố
    pi
    đều có số mũ chẵn, tức mọi
    ai
    chẵn, hay mọi
    ai+1
    đều là số lẻ. Khi này,
    d(x)
    lẻ.
  • Ngược lại, khi này tồn tại
    pi
    có số mũ lẻ, tức
    ai
    lẻ, hay
    ai+1
    là số chẵn. Khi này
    d(x)
    chẵn.

Bài toán quy về kiểm tra một số có phải số chính phương hay không.

Ta có thể kiểm tra một số

x có phải số chính phương hay không bằng cách lấy bình phương phần nguyên của
x
, nói cách khác, kiểm tra biểu thức sau đây có thỏa không:
x2=x

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

O(logn)

Đây là độ phức tạp của hàm sqrt() trong C++ STL.

Độ phức tạp thời gian của cả bài toán là tổng độ phức tạp thời gian của từng yêu cầu.

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

long long a, b, n;

long long lcm(long long a, long long b) {
    return a * b / __gcd(a, b);
}

bool sqr(long long x) {
    long long t = sqrt(x);
    return t * t == n;
}

int main() {
    cin >> a >> b >> n;
    cout << lcm(a, b) << '\n' << sqr(n);
    return 0;
}

Bài 3

Tóm tắt đề bài

Cho dãy số nguyên gồm

n phần tử
a1,a2,,an
. Yêu cầu:

  • Liệt kê mọi số nguyên dương chẵn
  • Tính giá trị nhỏ nhất của dãy
  • Kiểm tra xem dãy có gồm toàn số nguyên tố hay không
  • Tìm đoạn con liên tiếp có nhiều phần tử âm nhất
  • Tìm số nguyên dương nhỏ nhất không xuất hiện trong dãy

Liệt kê mọi số nguyên dương chẵn

Duyệt qua mọi

i[1,n], nếu
ai
chia hết cho 2 thì in ra
ai

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

O(n)

Tính giá trị nhỏ nhất của dãy

Gọi

fmin là giá trị nhỏ nhất của dãy, khởi gán
fmin=
. Duyệt qua mọi
i[1,n]
, nếu
ai<fmin
gán
fmin=ai
.

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

O(n)

Kiểm tra xem dãy có gồm toàn số nguyên tố hay không

Sử dụng thuật toán kiểm tra số nguyên tố trong căn bậc 2 hoặc sàng nguyên tố Eratosthenes để giải bài toán.

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

  • O(i=1nai)
    nếu kiểm tra trong căn bậc 2.
  • O(AlogA)
    với
    A=max(ai)
    nếu kiểm tra bằng sàng nguyên tố.

Tìm đoạn con liên tiếp có nhiều phần tử âm nhất

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

Gọi

f(i) là đoạn con có nhiều phần tử âm nhất kết thúc tại i. Khi này:
f(i)={ai<0,f(i1)+1ai0,0

Đáp án:

max(f(1),f(2),f(n)).

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

O(n)

Tìm số nguyên dương nhỏ nhất không xuất hiện trong dãy

Gọi

mark(x) biểu thị
x
có xuất hiện hay không:

  • mark(x)=0
    nếu x không tồn tại trong dãy.
  • Ngược lại,
    mark(x)=1
    .

Duyệt qua mọi

i[1,n], nếu
ai>0
thì đánh dấu
mark(ai)=1
.

Sau đó duyệt qua mọi giá trị

x[1,n+1], nếu
mark(x)=0
thì
x
là số nguyên dương nhỏ nhất không xuất hiện trong dãy.

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

O(n)

Độ phức tạp thời gian của cả bài toán là tổng độ phức tạp thời gian của từng yêu cầu.

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

int main() { 
    int n;
    vector<int> a;
    vector<bool> f, mark;

    cin >> n;
    
    a.resize(n);
    f.resize(n+5,true);
    mark.resize(n+5,false);
    
    for (auto &x : a) {
        cin >> x;
    }
    for (auto &x : a) {
        if (x > 0 && !(x & 1)) {
            cout << x << ' ';
        }
    }
    
    cout << '\n' << *min_element(a.begin(),a.end()) << '\n';
    
    f[0] = f[1] = 0;
    for (int i = 1, t = sqrt(n); i <= t; i++) {
        if (f[i]) {
            for (int j = i * i; j <= n; j += i) {
                f[j] = 0;
            }
        }
    }
    
    bool ans = 1;
    for (auto &x : a) {
        if (x < 0 || !f[x]) {
            ans = 0;
            break;
        }
    }
    
    cout << (ans ? "YES" : "NO") << '\n';

    int max_len = 0, cur_len = 0;
    for (auto &x : a) {
        if (x >= 0) {
            if (cur_len > max_len) max_len = cur_len;
            cur_len = 0;
        }
        else {
            cur_len++;
        }
    }
    
    if (cur_len > max_len) {
        max_len = cur_len;
    }
    
    cout << max_len << '\n';

    for (auto &x : a) {
        if (!mark[x] && x > 0) {
            mark[x] = 1;
        }
    }
    
    for (int i = 1; i <= n + 1; i++) {
        if (!mark[i]) {
            cout << i;
            break;
        }
    }
}

Bài 4: Công ty

Tóm tắt đề

Cho

n đoạn thẳng, đoạn thứ
i
kéo dài từ
ai
đến
bi
. Hỏi số lượng đoạn thẳng giao nhau tại cùng một điểm nhiều nhất là bao nhiêu.

Lời giải

Đây là một bài toán sweep line cơ bản.

Nhận xét

Chỉ điểm đầuđiểm cuối của mỗi đoạn sẽ ảnh hưởng tới số lượng đoạn thẳng giao nhau tại một điểm.

Gọi

f(x) là số lượng đoạn thẳng có chứa điểm
x
. Với mọi
i[1,n]
, tăng
f(ai)
lên
1
và giảm
f(bi+1)
đi
1
.

Biểu thị khi đến điểm

ai, đoạn thẳng thứ
i
bắt đầu xuất hiện, còn khi đến
bi+1
thì đoạn thẳng thứ
i
kết thúc.

Sau đó duyệt qua mọi điểm

xi có tồn tại trong mảng
f
theo thứ tự tăng dần, ta tính được
f(xi)
theo công thức sau
f(xi)=f(xi)+f(xi1)
.

Tại bất cứ thời điểm nào,

f(xi) cho ta biết số lượng đoạn thẳng đi qua điểm
xi
.

Đáp án là

max(f(x1),f(x2),,f(xk)).

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

O(nlogn)

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

int main() {
    int n;
    vector<array<int,2>> event;

    cin >> n;
    for (int i = 1; i <= n; i++) {
        int a, b;
        cin >> a >> b;
        event.push_back({a, 1});
        event.push_back({b+1, -1});
    }
    
    sort(event.begin(),event.end());
    int virus = 0, max_virus = 0;
    
    for (auto &e : event) {
        virus += e[1];
        max_virus = max(max_virus, virus);
    }
    
    cout << max_virus;
    
    return 0;
}