G. Mischievous Shooter

G. Mischievous Shooter

Once the mischievous and wayward shooter named Shel found himself on a rectangular field of size $n \times m$, divided into unit squares. Each cell either contains a target or not.

Shel only had a lucky shotgun with him, with which he can shoot in one of the four directions: right-down, left-down, left-up, or right-up. When fired, the shotgun hits all targets in the chosen direction, the Manhattan distance to which does not exceed a fixed constant $k$. The Manhattan distance between two points $(x_1, y_1)$ and $(x_2, y_2)$ is equal to $|x_1 - x_2| + |y_1 - y_2|$.


Possible hit areas for $k = 3$.

Shel's goal is to hit as many targets as possible. Please help him find this value.

Input

Each test consists of several test cases. The first line contains a single integer $t$ ($1 \le t \le 1000$) — the number of test cases. Then follows the description of the test cases.

The first line of each test case contains field dimensions $n$, $m$, and the constant for the shotgun's power $k$ ($1 \le n, m, k \le 10^5, 1 \le n \cdot m \le 10^5$).

Each of the next $n$ lines contains $m$ characters — the description of the next field row, where the character '.' means the cell is empty, and the character '#' indicates the presence of a target.

It is guaranteed that the sum of $n \cdot m$ over all test cases does not exceed $10^5$.

Output

For each test case, output a single integer on a separate line, which is equal to the maximum possible number of hit targets with one shot.

Example

input

4
3 3 1
.#.
###
.#.
2 5 3
###..
...##
4 4 2
..##
###.
#..#
####
2 1 3
#
#

output

3
4
5
2

Note

Possible optimal shots for the examples in the statement:

 

解题思路

  看完题就很快想到做法了,很明显就是枚举每个格子然后用前缀和求四个方向的三角形中 # 的数量,但想到会有很多边界情况代码很难写就不做了。

  为了少写些代码,我们可以固定一个三角形的方向,通过将矩阵顺时针旋转 $90^{\circ}$ 来实现其他方向的枚举过程,这样就可以用同一套代码逻辑来处理四个方向的问题。为了方便这里选择左下角的方向。

  三角形内的 # 用前缀和来维护,当从 $(i,j)$ 变到 $(i, j+1)$ 时,变化的部分入下图。绿色的部分是新增的,红色的部分是删掉的。

  所以我们只需维护两个前缀和,一个是列方向的用 $s_1[i][j]$ 表示第 $j$ 列的 $1 \sim i$ 行内 # 的数量,转移方程是 $s_1[i][j] = s_1[i-1][j] + [g_{i,j} = \#]$。另外一个是对角线方向的用 $s_2[i][j]$ 表示从 $(i,j)$ 沿截距为 $j-i$ 的直线左上方的 # 的数量,转移方程是 $s_2[i][j] = s_2[i-1][j-1] + [g_{i,j} = \#]$。

  用 $s$ 表示 $(i,j)$ 左下方三角形内 # 的数量,当枚举到下一个格子 $(i,j+1)$ 时,令 $j \gets j+1$,那么 $s$ 就变成 $s + (s_1[i+k][j] - s_1[i-1][j]) - (s_2[i+k][j-1] - s_2[i-1][j-k-2])$。考虑边界情况,当 $i+k > n$,在 $s_1$ 中只需变成 $n$ 即可。在 $s_2$ 中,令 $x = i+k$,$y = j-k-2$,则需要将 $x$ 减去 $i+k-n$ 变成 $n$,对应的 $y$ 也要减去 $i+k-n$。如果变化后的 $y$ 小于 $0$ 则变成 $0$ 即可。因此实际上 $s$ 应该变成 $$s + (s_1[\min\{n,i+k\}][j] - s_1[i-1][j]) - (s_2[\min\{n,i+k\}][\max\{0,j-1-\max\{0,i+k-n\}\}] - s_2[i-1][\max\{0,j-k-2\}])$$

  最后就是如何矩阵顺时针旋转 $90^{\circ}$。举个例子模拟一下,可以发现原本的 $g_{i,j}$ 就会变成 $f_{j,n-i-1}$。

  AC 代码代码如下,时间复杂度为 $O(n \cdot m)$:

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

void solve() {
    int n, m, k;
    cin >> n >> m >> k;
    vector<string> g(n);
    for (int i = 0; i < n; i++) {
        cin >> g[i];
    }
    int ret = 0;
    for (int i = 0; i < 4; i++) {
        vector<vector<int>> s1(n + 1, vector<int>(m + 1)), s2(n + 1, vector<int>(m + 1));
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                if (g[i - 1][j - 1] == '#') s1[i][j] = s2[i][j] = 1;
                s1[i][j] += s1[i - 1][j];
                s2[i][j] += s2[i - 1][j - 1];
            }
        }
        for (int i = 1; i <= n; i++) {
            int s = 0;
            for (int j = 1; j <= m; j++) {
                s += s1[min(n, i + k)][j] - s1[i - 1][j];
                s -= s2[min(n, i + k)][max(0, j - 1 - max(0, i + k - n))] - s2[i - 1][max(0, j - k - 2)];
                ret = max(ret, s);
            }
        }
        vector<string> f(m, string(n, 0));
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                f[j][n - i - 1] = g[i][j];
            }
        }
        g = f;
        swap(n, m);
    }
    cout << ret << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
    
    return 0;
}

 

参考资料

  Codeforces Round 920 (Div. 3) A-G 讲解:https://www.bilibili.com/video/BV19C4y1k7MA/

  Editorial for Codeforces Round 920 (Div. 3):https://codeforces.com/blog/entry/124757

posted @ 2024-01-24 21:46  onlyblues  阅读(36)  评论(0编辑  收藏  举报
Web Analytics