Hướng dẫn cho Giáo Sư Ba Lô
Chỉ sử dụng khi thực sự cần thiết như một cách tôn trọng tác giả và người viết hướng dẫn này.
Chép code từ bài hướng dẫn để nộp bài là hành vi có thể dẫn đến khóa tài khoản.
Chép code từ bài hướng dẫn để nộp bài là hành vi có thể dẫn đến khóa tài khoản.
Authors:
Hướng dẫn giải: Giáo Sư Ba Lô
Phân tích
Với mỗi số \( N \):
- Chuyển \( N \) sang chuỗi nhị phân \( S \)
- Xét mọi đoạn con của \( S \)
Một đoạn được gọi là "cân bằng" nếu:
- Độ dài chẵn
- Chia đôi ra:
- Số lượng '1' bên trái = số lượng '1' bên phải
Nhận xét quan trọng
Giả sử đoạn con có dạng:
\[
S[l..r], \quad \text{độ dài } = 2k
\]
Gọi:
- \( left = S[l..l+k-1] \)
- \( right = S[l+k..r] \)
Điều kiện:
\[
\text{count\_1}(left) = \text{count\_1}(right)
\]
Ý tưởng chính
Dùng prefix sum:
\[
pref[i] = \text{số lượng '1' từ } 1 \rightarrow i
\]
Khi đó:
\[
\text{count\_1}(l \rightarrow r) = pref[r] - pref[l-1]
\]
Sub 1 (50%): \( T \le 1000 \)
Cách làm brute force
- Sinh tất cả đoạn con
- Chỉ xét đoạn có độ dài chẵn
- Kiểm tra điều kiện
Độ phức tạp:
\[
O(n^3)
\]
Với \( n \le 20 \) (vì \( N \le 10^6 \)) nên vẫn chạy được
Code C++ (Sub 1)
C++
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
string s = "";
while (n > 0) {
s += char((n % 2) + '0');
n /= 2;
}
reverse(s.begin(), s.end());
int len = s.size();
bool ok = false;
for (int l = 0; l < len; l++) {
for (int r = l; r < len; r++) {
int length = r - l + 1;
if (length % 2 != 0) continue;
int mid = (l + r) / 2;
int c1 = 0, c2 = 0;
for (int i = l; i <= mid; i++)
if (s[i] == '1') c1++;
for (int i = mid + 1; i <= r; i++)
if (s[i] == '1') c2++;
if (c1 == c2) ok = true;
}
}
if (ok) cout << "YES\n";
else cout << "NO\n";
}
}
Sub 2 (100%): \( T \le 10^6 \)
Tối ưu
Nhận xét:
- Độ dài chuỗi nhị phân tối đa ~ 20
- Không thể \( O(n^3 \cdot T) \)
=> Tối ưu bằng prefix sum
Cách làm
- Tạo prefix sum
- Duyệt đoạn chẵn
- Tính nhanh số lượng '1'
Độ phức tạp mỗi test:
\[
O(n^2)
\]
Tổng:
\[
O(T \cdot n^2) \approx 10^6 \cdot 400 \Rightarrow OK
\]
Code C++ (Full)
C++
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
string s = "";
while (n > 0) {
s += char((n % 2) + '0');
n /= 2;
}
reverse(s.begin(), s.end());
int len = s.size();
vector<int> pref(len + 1, 0);
for (int i = 0; i < len; i++) {
pref[i + 1] = pref[i] + (s[i] == '1');
}
bool ok = false;
for (int l = 0; l < len; l++) {
for (int r = l; r < len; r++) {
int length = r - l + 1;
if (length % 2 != 0) continue;
int mid = (l + r) / 2;
int c1 = pref[mid + 1] - pref[l];
int c2 = pref[r + 1] - pref[mid + 1];
if (c1 == c2) {
ok = true;
break;
}
}
if (ok) break;
}
if (ok) cout << "YES\n";
else cout << "NO\n";
}
}
Code Python
Python
import sys
input = sys.stdin.readline
t = int(input())
for _ in range(t):
n = int(input())
s = bin(n)[2:]
len_s = len(s)
pref = [0] * (len_s + 1)
for i in range(len_s):
pref[i+1] = pref[i] + (s[i] == '1')
ok = False
for l in range(len_s):
for r in range(l, len_s):
length = r - l + 1
if length % 2 != 0:
continue
mid = (l + r) // 2
c1 = pref[mid+1] - pref[l]
c2 = pref[r+1] - pref[mid+1]
if c1 == c2:
ok = True
break
if ok:
break
print("YES" if ok else "NO")
Code Java
Java
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int t = sc.nextInt();
while (t-- > 0) {
int n = sc.nextInt();
String s = Integer.toBinaryString(n);
int len = s.length();
int[] pref = new int[len + 1];
for (int i = 0; i < len; i++) {
pref[i + 1] = pref[i] + (s.charAt(i) == '1' ? 1 : 0);
}
boolean ok = false;
for (int l = 0; l < len; l++) {
for (int r = l; r < len; r++) {
int length = r - l + 1;
if (length % 2 != 0) continue;
int mid = (l + r) / 2;
int c1 = pref[mid + 1] - pref[l];
int c2 = pref[r + 1] - pref[mid + 1];
if (c1 == c2) {
ok = true;
break;
}
}
if (ok) break;
}
System.out.println(ok ? "YES" : "NO");
}
}
}
Bình luận (1)