Page 1 SỞ GIÁO DỤC VÀ ĐÀO TẠO HẢI DƯƠNG ĐỀ CHÍNH THỨC KỲ THI CHỌN HỌC SINH GIỎI LỚP 12 THPT CẤP TỈNH NĂM HỌC 2022 – 2023 ĐÁP ÁN (Solutions) Đề thi môn TIN HỌC Bài 1 Đo chiều cao Sử dụng hệ thức lượng.
Trang 1Page 1
SỞ GIÁO DỤC VÀ ĐÀO TẠO
HẢI DƯƠNG
ĐỀ CHÍNH THỨC
KỲ THI CHỌN HỌC SINH GIỎI LỚP 12 THPT CẤP TỈNH
NĂM HỌC 2022 – 2023
ĐÁP ÁN (Solutions)
Đề thi môn: TIN HỌC -
Bài 1: Đo chiều cao
Sử dụng hệ thức lượng trong tam giác vuông ta có công thức đáp số là:
ℎ + 𝑎 × tan(𝛼) = ℎ + 𝑎 × sin(𝛼)
cos(𝛼)
1 Chương trình Pascal:
var
a, alpha, h: double;
BEGIN
read(a, alpha, h);
alpha:=alpha*pi/180;
write(h+a*sin(alpha)/cos(alpha):0:3);
END
2 Chương trình C++:
#include <bits/stdc++.h>
using namespace std;
int main() {
double a, alpha, h;
cin >> a >> alpha >> h;
alpha = alpha * acos(-1) / 180;
printf("%.3f", h + a * tan(alpha));
}
3 Chương trình Python
import math
a = float(input())
alpha = float(input())
h = float(input())
alpha = alpha*math.pi/180
Trang 2Page 2
print("{:.3f}".format(h + a*math.tan(alpha)))
Bài 2: Thi online
Sử dụng biến 𝑑 để lưu trữ số lần liên tiếp trước đó trả lời đúng khởi đầu 𝑑 = 0 Duyệt tất cả các ký tự từ đầu đến cuối dãy:
• Nếu ký tự gặp là ‘1’: Cộng vào kết quả 𝑑 + 1 sau đó tăng 𝑑
• Nếu ký tự gặp là ‘0’: Khởi tạo lại 𝑑 = 0
1 Chương trình Pascal
var
res: int64;
d: longint;
ch: char;
n, i: longint;
BEGIN
res:=0;
d:=0;
readln(n);
for i:=1 to n do
begin
read(ch);
res:=res + (d+1)*(ord(ch)-48);
if ch='1' then inc(d) else d:=0;
end;
writeln(res);
END
2 Chương trình C++
#include <bits/stdc++.h>
using namespace std;
int main() {
string s;
int n; cin >> n;
cin >> s;
long long ans = 0;
int d = 0;
for(int i = 0; s[i]; ++i) {
ans += (d + 1) * (s[i] - '0');
d = (s[i] - '0') ? d + 1 : 0;
}
cout << ans;
}
3 Chương trình Python
Trang 3Page 3
n = int(input())
a = input()
d = 0
ans = 0
for i in range(n) :
if a[i]=='1' :
ans = ans + (d+1)
d = d +1
else : d=0
print(ans)
Bài 3: Chọn quà
Nhận xét rằng số gói quà mà 𝑚 bạn nữ nhận sẽ chia thành 2 phần:
• Phần 1: Gồm 𝑘 gói quà liên tiếp tính từ trái
• Phần 2: Gồm 𝑚 − 𝑘 gói quà liên tiếp tính từ phải
Bài toán qui về tính:
max
+) Giải pháp 1: (Qua 5 tests đầu) Đơn giản là sử dụng vòng lặp để tính tống:
Tham khảo giải pháp này qua chương trình Pascal sau:
var
n, m, i, k, j: longint;
a: array[0 1000000] of longint;
ans, TL, TR: int64;
BEGIN
read(n, m);
for i:=1 to n do read(a[i]);
ans:=0;
for k:=0 to m do
begin
TL:=0;
for j:=1 to k do TL:=TL+a[j];
TR:=0;
for j:=n-(m-k)+1 to n do TR:=TR + a[j];
if TL+TR>ans then ans:=TL+TR;
end;
writeln(ans);
END
+) Giải pháp 2: (Full điểm)
Chuẩn bị trước mảng 𝑠0, 𝑠1, … , 𝑠𝑛 với 𝑠𝑖 = 𝑎1+ 𝑎2 + ⋯ + 𝑎𝑖 = 𝑠𝑖−1 + 𝑎𝑖
Khi đó 𝑎1+ ⋯ + 𝑎𝑘 = 𝑠𝑘 còn 𝑎𝑛−(𝑚−𝑘)+1+ ⋯ 𝑎𝑛 = 𝑠𝑛 − 𝑠𝑛−(𝑚−𝑘)
và ta chỉ cần một vòng lặp Chương trình pascal dưới đây thực hiện ý tưởng này:
var
n, m, i: longint;
Trang 4Page 4
a: array[0 1000000] of longint;
s: array[0 1000000] of int64;
ans, T : int64;
k: longint;
BEGIN
read(n, m);
for i:=1 to n do read(a[i]);
s[0]:=0;
for i:=1 to n do s[i]:=s[i-1]+a[i];
ans:=0;
for k:=0 to m do
begin
T:=s[k]+s[n]-s[n-(m-k)];
if T>ans then ans:=T;
end;
writeln(ans);
END
1 Giải pháp tương tự bằng C++ (full điểm):
#include <bits/stdc++.h>
#define maxn 1000001
using namespace std;
int a[maxn];
long long s[maxn];
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
int n, m;
cin >> n >> m;
for(int i=1;i<=n;++i) cin >> a[i];
s[0]=0;
for(int i=1;i<=n;++i) s[i] = s[i-1] +a[i];
long long ans=0;
for(int k=0;k<=m;++k) {
long long T = s[k]+s[n]-s[n-(m-k)];
ans = max(ans,T);
}
cout << ans;
}
2 Giải pháp tương tự bằng Python (full điểm)
n, m = map(int,input().split())
a=[0]
a.extend([int(x) for x in list(input().split())])
Trang 5Page 5
s=[0]*(n+1)
for i in range(1,n+1) : s[i]=s[i-1]+a[i]
ans=0
for k in range(m+1) :
T = s[k]+s[n]-s[n-(m-k)]
if T> ans : ans = T
print(ans)
Bài 4: Phân tích số
*) Nếu sử dụng ba vòng lặp lồng nhau qua được 2 tests Bằng cách nhận xét rằng khi biết 𝑥, 𝑦 ta có thể tính 𝑧 = 𝑛 − 𝑥 − 𝑦 do vậy chỉ cần 2 vòng lặp lồng nhau Sử dụng ý tưởng này sẽ qua được 5 tests như chương trình Pascal dưới đây:
var
n, x, y, z: longint;
ans: int64;
BEGIN
assign(input,'BAI4.inp'); reset(input);
assign(output,'BAI4.out'); rewrite(output);
read(n);
ans:=0;
for x:=1 to n do
begin
for y:=x+1 to n do
begin
z:=n-x-y;
if z<=y then break;
inc(ans);
end;
end;
writeln(ans);
END
*) Giải pháp full điểm
Trước tiên ta cần tính số bộ 𝑥, 𝑦, 𝑧 nguyên dương khác nhau thỏa mãn 𝑥 + 𝑦 + 𝑧 = 𝑛 Gọi số lượng này là 𝑇 Khi đó vì mỗi bộ (𝑥, 𝑦, 𝑧) ứng với 6 hoán vị khác nhau nên kết quả tìm được là 𝑇
Bài toán qui về tính 𝑇 Ta có:
𝑇 = 𝑃 − 𝑄
Ở đây:
Trang 6Page 6
• 𝑃 là số bộ (𝑥, 𝑦, 𝑧) nguyên dương thỏa mãn 𝑥 + 𝑦 + 𝑧 = 𝑛 (không phân biệt khác nhau hay không)
• 𝑄 là số bộ nguyên dương 𝑥 + 𝑦 + 𝑧 = 𝑛 trong đó có ít nhất hai giá trị bằng nhau (hoặc 𝑥 = 𝑦 hoặc 𝑥 = 𝑧 hoặc 𝑦 = 𝑧)
+) Đầu tiên ta tính 𝑃
Nhận xét rằng số cặp nguyên dương (𝑢, 𝑣) thỏa mãn 𝑢 + 𝑣 = 𝑘 là 𝑘 − 1 (lần lượt với
𝑢 = 1,2, … , 𝑘 − 1; mỗi giá trị 𝑢 có một giá trị 𝑣
Do vậy ta có:
• Với 𝑥 = 1 có 𝑛 − 2 cặp (𝑦, 𝑧)
• Với 𝑥 = 2 có 𝑛 − 3 cặp (𝑦, 𝑧)
•
• Với 𝑥 = 𝑛 − 2 có 𝑛 − (𝑛 − 2 + 1) = 1 cặp (𝑦, 𝑧)
Do đó số bộ nguyên dương (𝑥, 𝑦, 𝑧) thỏa mãn 𝑥 + 𝑦 + 𝑧 = 𝑛 là:
𝑃 = 1 + 2 + ⋯ + (𝑛 − 2) = (𝑛 − 2)(𝑛 − 1)
2 +) Tiếp theo ta tính 𝑄
• Số bộ nguyên dương (𝑥, 𝑦, 𝑧) có 𝑥 = 𝑦 là [𝑛
2] − 1 (lần lượt thay 𝑥 = 𝑦 =
1, 2, … , [𝑛
2] − 1)
• Tương tự số bộ (𝑥, 𝑦, 𝑧) có 𝑦 = 𝑧 là [𝑛
2] − 1
• và số bộ (𝑥, 𝑦, 𝑧) có 𝑥 = 𝑧 cũng là [𝑛
2] − 1
Do vậy:
𝑄 = 3 × ([ 𝑛
2 ] − 1) Tuy nhiên khi 𝑛 chia hết cho ba thì bộ 𝑥 = 𝑦 = 𝑧 = 𝑛
3 được đếm ba lần trong khi tính
Q như trên (trong khi đó bộ này chỉ được tính một lần) Trong trường hợp này:
𝑄 = 3 × ([ 𝑛
2 ] − 1) − 2 Chương trình Pascal dưới đây minh họa ý tưởng trên (full điểm):
var T, n: int64;
BEGIN
assign(input,'BAI4.inp'); reset(input);
assign(output,'BAI4.out'); rewrite(output);
read(n);
T:=(n-2)*(n-1) div 2 -3*(n div 2-1);
if n mod 3=0 then T:=T+2;
writeln(T div 6);
END
Trang 7Page 7
1 Chương trình tương tự bằng C++ (full điểm):
#include <bits/stdc++.h>
using namespace std;
#define task "BAI4"
int main() {
freopen(task".inp", "r", stdin);
freopen(task".out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
long long n;
cin >> n;
long long T = (n % 3) ? (n - 2) * (n - 1) / 2 - 3 * (n / 2 - 1) :
(n - 2) * (n - 1) / 2 - 3 * (n / 2 - 1)+2;
cout << T / 6;
}
2) Chương trình Python (full điểm)
with open("BAI4.inp","r") as fin :
n=int(fin.readline())
ans = (n-2) * (n-1) // 2 - 3 * (n//2 - 1)
if n % 3 == 0 : ans = ans + 2
with open("BAI4.out","w") as fout :
fout.write(str(ans // 6))
Bài 5: Tổng giá trị
Bài toán yêu cầu tính tổng:
∑ 𝑎𝑖 × 𝑎𝑗 × 𝑎𝑘
1≤𝑖<𝑗<𝑘≤𝑛
Giải pháp đơn giản là sử dụng ba vòng lặp Khi đó ta qua được 2 tests Chương trình pascal dưới đây minh họa ý tưởng này:
const
P = 1000000007;
var n, i, j, k: longint;
a: array[0 1000000] of longint;
ans: int64;
BEGIN
assign(input,'BAI5.inp'); reset(input);
assign(output,'BAI5.out'); rewrite(output);
read(n);
Trang 8Page 8
for i:=1 to n do read(a[i]);
ans:=0;
for i:=1 to n do
for j:=i+1 to n do
for k:=j+1 to n do
ans := (ans + int64(a[i])*a[j]*a[k]) mod P;
writeln(ans);
END
Ta có thể cải tiến bằng nhận xét nếu 𝑗 < 𝑘 cố định thì:
∑ 𝑎𝑖 × 𝑎𝑗 × 𝑎𝑘
𝑖<𝑗
= (𝑎1 + 𝑎2+ ⋯ + 𝑎𝑗−1) × 𝑎𝑗 × 𝑎𝑘
Do vậy chỉ cần sử dụng 2 vòng lặp lồng nhau (khi đó qua được 5 tests) như trong chương trình pascal dưới đây:
const
P=1000000007;
var n, i, j, k: longint;
a: array[0 1000000] of longint;
ans, T: int64;
BEGIN
assign(input,'BAI5.inp'); reset(input);
assign(output,'BAI5.out'); rewrite(output);
read(n);
for i:=1 to n do read(a[i]);
ans:=0;
for j:=2 to n-1 do
begin
T:=0;
for i:=1 to j-1 do T:=(T+a[i]) mod P;
for k:=j+1 to n do
begin
ans:=(ans+T*a[j]*a[k]) mod P;
end;
end;
writeln(ans);
END
Để được full điểm ta cần phải có giải thuật 𝑂(𝑛) Nhận xét rằng:
∑ ∑ 𝑎𝑖 × 𝑎𝑗
𝑗−1
𝑖=1
= ∑ (𝑎𝑗 × ∑ 𝑎𝑖
𝑗−1
𝑖=1
) = ∑ 𝑎𝑗 × 𝑠𝑗−1
𝑢
𝑗=2
𝑢
𝑗=2
𝑢
𝑗=2
Với 𝑠𝑖 = 𝑎1+ ⋯ + 𝑎𝑖 = 𝑠𝑖−1 + 𝑎𝑖
Do vậy bằng cách dùng mảng tổng tiền tố ta có thể tính tổng :
Trang 9Page 9
∑ 𝑎𝑖 × 𝑎𝑗
1≤𝑖<𝑗<𝑘
Trong 𝑂(𝑛) Cuối cùng chỉ còn một vòng lặp 𝑘 Chương trình pascal dưới đây minh họa ý tưởng này (full điểm):
const
maxn = 1000000;
P = 1000000007;
var n, i: longint;
a: array[0 maxn] of longint;
s: int64;
b: array[0 maxn] of int64;
res: int64;
BEGIN
assign(input,'BAI5.inp'); reset(input);
assign(output,'BAI5.out'); rewrite(output);
read(n);
for i:=1 to n do read(a[i]);
s:=a[1] mod P;
b[1]:=0;
for i:=2 to n do
begin
b[i]:=(b[i-1]+s*a[i]) mod P;
s:=(s+a[i]) mod P;
end;
res:=0;
for i:=3 to n do
begin
res:=(res + b[i-1]*a[i]) mod P;
end;
writeln(res);
END
1 Chương trình tương tự bằng C++ (full điểm):
#include <bits/stdc++.h>
#define maxn 1000005
#define MOD 1000000007
using namespace std;
int n, a[maxn];
long long b[maxn];
#define task "BAI5"
Trang 10Page 10
int main() {
freopen(task".inp", "r", stdin);
freopen(task".out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
for(int i = 1; i <= n; ++i)
cin >> a[i];
long long s = a[1] % MOD;
b[1] = 0;
for(int i = 2; i <= n; ++i) {
b[i] = (b[i - 1] + s * a[i]) % MOD;
s =(s+ a[i]) % MOD;
}
long long res = 0;
for(int i = 1; i <= n; ++i) {
res =(res + a[i] * b[i - 1]) % MOD;
}
cout << res;
}
2 Chương trình Python (full điểm)
MOD = 1000000007
a=[0]
with open("BAI5.inp","r") as fin :
n = int(fin.readline())
a.extend([int(x) for x in list(fin.readline().split())])
b=[0]*(n+1)
s=a[1]
for i in range(2,n+1) :
b[i]=(b[i-1] + s * a[i]) % MOD
s = (s + a[i]) % MOD
ans = 0
for i in range(3,n+1) :
ans = (ans + b[i-1] * a[i]) % MOD
with open("BAI5.out","w") as fout :
fout.write(str(ans))