開啟章節選單
CSES Forest Queries
解題思路
如果使用暴力判斷,時間複雜度為 ,不夠快。
這時,我們可以使用二維前綴和來優化這個計算,時間複雜度為 預處理、 回答每一次查詢,能在時限內通過。
更精確來說,因為我們要求一個區塊內有多少樹(*
),可以將 *
的位置定義為 (一棵樹),.
的位置定義為 (零棵樹)。將字元改成數字以後,便可以用之前教過的方法求出前綴和。
定義陣列
vector<string> input(n); ... vector<vector<int>> val(n, vector<int>(n)); for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (input[i][j] == '*') { val[i][j] = 1; } } }
預處理前綴和
vector<vector<int>> psum(n + 1, vector<int>(n + 1)); for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { psum[i][j] = psum[i - 1][j] + psum[i][j - 1] - psum[i - 1][j - 1] + val[i - 1][j - 1]; } }
註:因為此程式碼的 二維陣列為 0-based,在處理前綴和的時候要在 中多扣 。
計算
使用二維前綴和陣列求出區塊和:
int sum = psum[y2][x2] - psum[y1 - 1][x2] - psum[y2][x1 - 1] + psum[y1 - 1][x1 - 1];
常見錯誤
0 / 1 based
請自己注意自己陣列定義及題目敘述的陣列 / 值是從 開始還是從 開始(通常題目都是 1-based)。
輸入順序
此題的輸入順序並非 x1, x2, y1, y2
或是 x1, y1, x2, y2
,而是 y1, x1, y2, x2
。實作的時候務必要多注意!
完整程式碼
#include <iostream> #include <vector> using namespace std; int main() { int n, q; cin >> n >> q; vector<string> input(n); for (int i = 0; i < n; i++) { cin >> input[i]; } vector<vector<int>> val(n, vector<int>(n)); for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (input[i][j] == '*') { val[i][j] = 1; } } } vector<vector<int>> psum(n + 1, vector<int>(n + 1)); for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { psum[i][j] = psum[i - 1][j] + psum[i][j - 1] - psum[i - 1][j - 1] + val[i - 1][j - 1]; } } for (int i = 0; i < q; i++) { int y1, x1, y2, x2; cin >> y1 >> x1 >> y2 >> x2; int sum = psum[y2][x2] - psum[y1 - 1][x2] - psum[y2][x1 - 1] + psum[y1 - 1][x1 - 1]; cout << sum << "\n"; } }