Hướng dẫn giải của Bật tắt điều hoà_VP10
Nộp một lời giải chính thức trước khi tự giải là một hành động có thể bị ban.
Lời giải này đang bị ẩn cho đến khi bạn chọn mở ra.
Chúng tôi khuyên bạn nên tự thử giải bài trước. Việc mở lời giải có thể làm lộ mất ý tưởng chính trước khi bạn có cơ hội tự giải.
Bạn phải đăng nhập để mở lời giải này.
Đăng nhậpTác giả: , , ,
Hiểu bài toán
Bài toán yêu cầu mô phỏng trạng thái của n chiếc điều hòa. Ban đầu tất cả đều tắt (0). Có q thao tác, mỗi thao tác gồm hai số nguyên x, y (1 ≤ x ≤ y ≤ n), biểu thị việc bật/tắt tất cả các điều hòa từ vị trí x đến y (nghĩa là nếu đang tắt thì bật, đang bật thì tắt). Sau khi thực hiện xong q thao tác, in ra trạng thái cuối cùng của từng chiếc điều hòa (0 là tắt, 1 là bật).
Các cách tiếp cận
Cách Brute Force (Mô phỏng trực tiếp)
#include <iostream>
using namespace std;
void AC() {
int n,q; cin >> n >> q;
int a[n+1];
for(int i = 1; i <= n; i++) a[i] = 0;
while(q--) {
int x,y; cin >> x >> y;
for(int i = x; i <= y; i++) {
a[i] = 1 - a[i];
}
}
for(int i = 1; i <= n; i++) cout << a[i] << ' ';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
AC();
return 0;
}
- Time Complexity: O(n * q)
- Space Complexity: O(n)
Cách tiếp cận này sử dụng một mảng để lưu trạng thái của các điều hòa. Với mỗi thao tác (x, y), vòng lặp chạy từ x đến y để cập nhật trạng thái (toggled: 0 thành 1, 1 thành 0).
- Ưu điểm: Đơn giản, dễ hiểu, đúng với mô tả bài toán.
- Nhược điểm: Nếu n và q lớn (ví dụ n, q ~ 10^5), độ phức tạp thời gian O(n*q) sẽ quá chậm (khoảng 10^10 thao tác), dẫn đến TLE (Time Limit Exceeded).
Cách Difference Array (Xấp xỉ)
#include <bits/stdc++.h>
#define ll long long
using namespace std;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
// freopen("cau2.inp","r",stdin);
// freopen("cau2.out","w",stdout);
int n,k,a,b;
cin>>n>>k;
vector<int>diff(n+1,0);
for(int i=0;i<k;++i){
cin>>a>>b;
diff[a-1]++; // Using 0-based indexing logic for diff array
diff[b]--;
}
int cur=0;
for(int i=0;i<n;++i){
cur+=diff[i];
cout<<cur%2<<" ";
}
return 0;
}
- Time Complexity: O(n + k)
- Space Complexity: O(n)
Thay vì cập nhật từng phần tử trong khoảng [x, y], ta sử dụng kỹ thuật Difference Array.
- Với mỗi truy vấn [x, y] (input 1-based), ta đánh dấu sự thay đổi:
- Tăng
diff[x-1]lên 1 (bắt đầu). - Giảm
diff[y]đi 1 (kết thúc).
- Tăng
- Sau khi xử lý hết các truy vấn, ta duyệt mảng
diffvà tính tổng tiền tố (prefix sum). Biếncurlưu số lượng thao tác bao trùm lên vị trí hiện tại. - Trạng thái cuối cùng của vị trí i là
cur % 2(vì mỗi thao tác là một lần lật). - Đặt
diffsizen+1để tránh tràn mảng khib == n. - Độ phức tạp: O(n + k) cho k truy vấn, rất nhanh.
Cách Prefix Sum (Tổng tiền tố)
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n, q;
cin >> n >> q;
vector<int> air_conditioners(n, 0);
for (int i = 0; i < q; ++i) {
int x, y;
cin >> x >> y;
// Adjust indices to be 0-based
x--;
y--;
for (int j = x; j <= y; ++j) {
air_conditioners[j] = 1 - air_conditioners[j];
}
}
for (int i = 0; i < n; ++i) {
cout << air_conditioners[i] << (i == n - 1 ? "" : " ");
}
cout << endl;
return 0;
}
- Time Complexity: O(n * q)
- Space Complexity: O(n)
Đây là cách tiếp cận Brute Force (Solution 3).
- Tạo mảng
air_conditioners. - Với mỗi truy vấn [x, y], lặp từ x đến y để lật trạng thái.
- Rõ ràng là cách này chậm nhất và chỉ phù hợp với dữ liệu nhỏ.
Phân tích độ phức tạp
| Cách tiếp cận | Time | Space | Tên |
|---|---|---|---|
| 1 | O(n * q) | O(n) | Brute Force (Mô phỏng trực tiếp) |
| 2 | O(n + k) | O(n) | Difference Array (Xấp xỉ) |
| 3 | O(n * q) | O(n) | Prefix Sum (Tổng tiền tố) |
Bài học kinh nghiệm
- Bài toán là bài toán cập nhật khoảng và truy vấn trạng thái cuối cùng. Kỹ thuật Difference Array (hoặc Prefix Sum) là tối ưu nhất cho các bài toán dạng 'cập nhật vùng, truy vấn tổng/số lần'.
- Việc lật trạng thái (0->1, 1->0) tương đương với việc cộng 1 modulo 2. Do đó, ta có thể đếm số lần mỗi vị trí được bao phủ bởi các truy vấn, sau đó lấy số đó chia 2 lấy dư.
Lỗi thường gặp
- Lỗi chỉ số mảng (Off-by-one error): Input là 1-based (từ 1 đến n), nhưng code C++ thường dùng 0-based. Cần chuyển đổi
x--trước khi dùng làm chỉ số, hoặc dùngdiffarray sizen+1và thao tác cẩn thận. - Tràn số nguyên: Nếu dùng mảng
intđể lưu tổng tiền tố và số truy vấn lớn, có thể tràn số. Tuy nhiên, trong bài này chỉ cần chia 2 lấy dư nên giá trị lưu trữ không cần quá lớn, nhưng vẫn nên chú ý.
Bình luận