// Gemini 3 #include #include #include using namespace std; int main() { // 高速入出力 ios_base::sync_with_stdio(false); cin.tie(NULL); int H, W; if (!(cin >> H >> W)) return 0; vector S(H); for (int i = 0; i < H; ++i) { cin >> S[i]; } // 1マスカギであるかをあらかじめ判定しておく (2次元配列) // 1マスカギ: 白マスかつ上下左右が黒マスまたは盤面の外 vector> is_one(H, vector(W, 0)); for (int i = 0; i < H; ++i) { for (int j = 0; j < W; ++j) { if (S[i][j] != '.') continue; bool u = (i == 0 || S[i - 1][j] == '#'); bool d = (i == H - 1 || S[i + 1][j] == '#'); bool l = (j == 0 || S[i][j - 1] == '#'); bool r = (j == W - 1 || S[i][j + 1] == '#'); if (u && d && l && r) { is_one[i][j] = 1; } } } // 2次元累積和 (1マスカギ用) vector> sum_one(H + 1, vector(W + 1, 0)); for (int i = 0; i < H; ++i) { for (int j = 0; j < W; ++j) { sum_one[i + 1][j + 1] = sum_one[i][j + 1] + sum_one[i + 1][j] - sum_one[i][j] + is_one[i][j]; } } auto get_one = [&](int A, int B, int C, int D) { return sum_one[C + 1][D + 1] - sum_one[A][D + 1] - sum_one[C + 1][B] + sum_one[A][B]; }; int Q; if (!(cin >> Q)) return 0; while (Q--) { int A, B, C, D; cin >> A >> B >> C >> D; --A; --B; --C; --D; // 0-indexed long long ans = 0; // 1. 1マスカギの集計 ans += get_one(A, B, C, D); // 2. 横のカギの集計 (領域内の各行について判定) // 横のカギ条件: 白マスかつ (左が領域外or#) かつ (右が白マスかつ領域内) for (int i = A; i <= C; ++i) { for (int j = B; j <= D; ++j) { if (S[i][j] != '.') continue; if (is_one[i][j]) continue; // 1マスカギは除外 bool left_ok = (j == B || S[i][j - 1] == '#'); bool right_ok = (j < D && S[i][j + 1] == '.'); if (left_ok && right_ok) { ans++; } } } // 3. 縦のカギの集計 (領域内の各列について判定) // 縦のカギ条件: 白マスかつ (上が領域外or#) かつ (下が白マスかつ領域内) for (int j = B; j <= D; ++j) { for (int i = A; i <= C; ++i) { if (S[i][j] != '.') continue; if (is_one[i][j]) continue; // 1マスカギは除外 bool up_ok = (i == A || S[i - 1][j] == '#'); bool down_ok = (i < C && S[i + 1][j] == '.'); if (up_ok && down_ok) { ans++; } } } // 4. 横のカギかつ縦のカギを両方満たすもの(重複分の調整) for (int i = A; i <= C; ++i) { for (int j = B; j <= D; ++j) { if (S[i][j] != '.') continue; if (is_one[i][j]) continue; bool left_ok = (j == B || S[i][j - 1] == '#'); bool right_ok = (j < D && S[i][j + 1] == '.'); bool up_ok = (i == A || S[i - 1][j] == '#'); bool down_ok = (i < C && S[i + 1][j] == '.'); if (left_ok && right_ok && up_ok && down_ok) { ans--; // 横と縦の両方でカウントされてしまっているため1つ引く } } } cout << ans << "\n"; } return 0; }