[ABC311] D~G 题解

发布时间 2023-08-10 13:03:45作者: MoyouSayuki

[ABC311] D~G 题解

D - Grid Ice Floor 搜索

题目的意思实际上是要求出所有可能到达的点,也就是说所有路径可达点的并。

基本上看清题目就会了,直接搜索每个点,每次枚举四个方向的时候直接冲到底,需要用数组去重贡献。

void dfs(int x, int y, int fax, int fay) {
    if(st[x][y]) return ;
    st[x][y] = 1;
    for(int i = 0; i < 4; i ++) {
        int cnt = 0;
        int xx = x + dx[i], yy = y + dy[i];
        while(check(xx, yy)) {
            ans += !vis[xx][yy], vis[xx][yy] = 1;
            xx += dx[i], yy += dy[i];
        }
        xx -= dx[i], yy -= dy[i];
        dfs(xx, yy, x, y);
    }
}

signed main() {
    memset(dist, 0, sizeof dist);
    vis[2][2] = 1;
    dist[2][2] = 1;
    dfs(2, 2, 0, 0);
    cout << ans << '\n';
    return 0;
}

E - Defect-free Squares

使用 \(dp[i][j]\) 表示以 \((i, j)\) 为右下角的正方形的最大边长是多少。

\[dp[i][j] = \min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]) + 1, (i, j) 合法\\ dp[i][j] = 0, (i, j) 非法 \]

F - Yet Another Grid Task

使用 \(dp[i][s]\) 表示第 \(i\) 列的状态为 \(s\) 的方案数。

这样会爆,考虑优化状态表示。

观察发现,如果一个位置是黑色的,那么它所在列下面的所有的格子都是黑的,所以只需要保留一个最上的黑格子位置即可,有了这个观察,得到表示:

\(dp[i][j]\) 表示第 \(i\) 列的最上黑色格子在第 \(j\) 行。

由于第 \(i\) 列的最高位是 \(j\),所以第 \(i - 1\) 列的最高位不能超过 \(j + 1\),否则第 \(i\) 列第 \(j + 1\) 行一定是黑色的,与最高黑色位是 \(j\) 的假设矛盾了。

如果令 \(top[i]\) 表示第 \(i\) 列初始最高黑色位,得到转移方程:

\[dp[i][j] = \sum_{k = top[i - 1]}^{\min(n, j + 1)} dp[i - 1][k] \]

发现可以前缀和优化掉,滚掉一维,剩下没了。

时间复杂度:\(O(nm)\)

#include <algorithm>
#include <cstring>
#include <iostream>
#include <queue>

using namespace std;

const int N = 2e3 + 10, mod = 998244353;

int n, m, top[N];
long long dp[N], s[N][N];
char g[N][N];

int main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            cin >> g[i][j], (g[i][j] == '#' ? top[j] = max(top[j], n - i + 1) : 0);
    for (int i = 0; i <= n; i++)
        s[0][i] = 1;
    for (int i = 1; i <= m; i++)
        for (int j = top[i]; j <= n; j++)
            dp[j] = s[i - 1][min(j + 1, n)] - s[i - 1][max(top[i - 1] - 1, 0)],
            s[i][j] = ((j ? s[i][j - 1] : 0) + dp[j]) % mod;
    cout << s[m][n] << '\n';
    return 0;
}

G - One More Grid Task

观察到 \(A_{i, j}\le 300\),考虑枚举矩形的最小值,之后将所有小于最小值的元素都设为非法,剩下就是一个最大子矩形的板子了,顶多套个二维前缀和。

时间复杂度:\(O(MAN)\)

#include <algorithm>
#include <cstring>
#include <iostream>
#include <queue>

using namespace std;

const int N = 3e2 + 10, M = 3e2;

int n, m, l[N][N], r[N][N], u[N][N];
int g[N][N];
long long s[N][N];
bool st[N];

long long sum(int a, int b, int c, int d)
{
    if (c < a || d < b)
        return 0;
    return s[c][d] - s[a - 1][d] - s[c][b - 1] + s[a - 1][b - 1];
}

int main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            cin >> g[i][j], s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + g[i][j], st[g[i][j]] = 1;
    long long ans = 0;
    for (int k = 1; k <= M; k++)
    {
        if (!st[k])
            continue; // 小优化
        memset(u, 0, sizeof u);
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= m; j++)
            {
                if (g[i][j] < k)
                    g[i][j] = 0;
                l[i][j] = r[i][j] = j;
            }
        for (int i = 1; i <= n; i++)
        {
            for (int j = 2; j <= m; j++)
                if (g[i][j] && g[i][j - 1])
                    l[i][j] = l[i][j - 1];
            for (int j = m - 1; j; j--)
                if (g[i][j] && g[i][j + 1])
                    r[i][j] = r[i][j + 1];
        }
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= m; j++)
            {
                if (!g[i][j])
                    continue;
                if (g[i - 1][j])
                    l[i][j] = max(l[i][j], l[i - 1][j]), r[i][j] = min(r[i][j], r[i - 1][j]);
                u[i][j] = u[i - 1][j] + 1;
                ans = max(ans, 1ll * k * sum(i - u[i][j] + 1, l[i][j], i, r[i][j]));
            }
        }
    }
    cout << ans << '\n';

    return 0;
}