P8865 [NOIP2022] 种花

P8865 NOIP2022 种花 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

\(a(x, y)\) 代表原文的 \(a_{x, y}\)

考虑对每个点统计:左上角在该点的 \(\texttt{C-}\)\(\texttt{F-}\) 的数量。最终答案累加每个点对应的答案即可。

那么接下来我们想:对于一个点怎么计算对应的答案?我们生成一个地图来看看:

其中白色代表可以放置花,红色代表不可放置。现在,我们要计算左上角在 \((1, 1)\)\(\texttt{C-}\) 的数量。

较容易看出,\(\texttt{C-}\) 的最上面那一横总共有 \(3\) 种画法,下面是其中一种:

考虑 \(3\) 的意义,其实它是从 \((1, 1)\) 开始,往右最多有几个连续的白色方块(不统计 \((1, 1)\) 本身)。

据此,我们考虑预处理 \(r(x, y)\) 表示 \((x, y)\) 开始向右最多有几个连续的 \(0\)(不统计 \((x, y)\) 本身)。特别地,当 \(a(x, y) = 1\) 时,记 \(r(x, y) = -1\)

\(r\) 可以通过 \(\Theta(nm)\) 的递推(或者说后缀和)得到,具体来说,当 \(a(x, y) = 1\) 时,\(r(x, y) = -1\),否则 \(r(x, y) = r(x, y + 1) + 1\)。边界是 \(r(x, m +1) = -1\)

那么 \(\texttt{C-}\) 的剩下一部分(先下再右的整个一个折线)有多少种画法呢?我们发现:

  • \(\texttt{C-}\) 的一竖延伸到 \((3, 1)\) 时,剩下一部分有 \(r(3, 1) = 3\) 种画法;
  • \(\texttt{C-}\) 的一竖延伸到 \((4, 1)\) 时,剩下一部分有 \(r(4, 1) = 0\) 种画法;
  • \(\texttt{C-}\) 的一竖延伸到 \((5, 1)\) 时,剩下一部分有 \(r(5, 1) = 5\) 种画法;
  • \(\texttt{C-}\) 的一竖延伸到 \((6, 1)\) 时,剩下一部分有 \(r(6, 1) = 1\) 种画法。

因此剩下这部分总共有 \(r(3, 1) + r(4, 1) + r(5, 1) + r(6, 1) = 9\) 种画法。根据乘法原理可以得到,以 \((1, 1)\) 为左上角的 \(\texttt{C-}\) 数量有 \(3 \times 9 = 27\) 个。

一般地,\((x, y)\) 对应的 \(\texttt{C-}\) 数量可以表示为 \(r(x, y) \times \sum\limits_{t = x + 2} ^ k r(t, y)\),其中 \(k\) 满足:\(a(x, y) = a(x + 1, y) = \cdots = a(k, y) = 0\),而 \(a(k + 1, y) = 1\),此处我们认为 \(a(n + 1, y) = 1\)

这里 \(k\) 的存在是考虑 \(\texttt{C-}\) 的那一竖,有时并不能像上面那个例子一样无限向下延伸,如果遇到一个红色方块就需要停止了。比如,若上面的 \((5, 1)\) 是红色,那么 \((1, 1)\) 对应的 \(\texttt{C-}\) 中,除去上面一横的剩下一部分,只剩下 \(r(3, 1) +r(4, 1) = 3\) 种画法。

为了让式子仍然可以 \(\Theta(1)\) 直接计算,我们考虑预处理 \(g(x, y) = \sum \limits _{t=x} ^ k r(t, y)\)\(k\) 的含义和上面相同。递推和 \(r\) 差不多:当 \(a(x, y) = 1\) 时,\(g(x, y) = 0\),否则 \(g(x, y) = g(x + 1, y) +r(x, y)\)。边界是 \(g(n + 1, y) = 0\)

这样以来,当 \(a(x, y) = 0\)\(a(x + 1, y) = 0\) 时,\((x, y)\) 对应的 \(\texttt{C-}\) 数量就为 \(r(x, y) \times g(x + 2, y)\),否则显然为 \(0\)请注意检验 \(a(x + 1, y)\)\(0\) 的必要性

接下来计算 \(\texttt{F-}\)。还是上面这个例子,上面那一横的画法显然还是 \(r(1, 1) = 3\)

剩下一部分因为形状的变化,画法数量也会有变化。当 \(\texttt{F-}\) 的上半竖延伸到 \((3, 1)\) 时,第二个横有 \(r(3, 1) = 3\) 种画法,这个仍然不变。但此时我们还要考虑下半竖,总共还有 \(6 - 3 = 3\) 种画法。因此,当 \(\texttt{F-}\) 的上半竖延伸到 \((3, 1)\) 时,除了第一横的剩下一部分总共有 \(3 \times 3 = 9\) 种画法。而当 \(\texttt{F-}\) 上半竖延伸到 \((5, 1)\) 时,总共有 \(r(5, 1) \times (6 - 5) = 5\) 种。其实也就是在 \(\texttt{C-}\) 的基础上,再套一个乘法原理将下半竖的画法统计进去。

类似一般地,\((x, y)\) 对应的 \(\texttt{F-}\) 数量可以表示为 \(r(x, y) \times \sum \limits _{t = x +2} ^ k r(t, y) \times (k - t)\),其中 \(k\) 满足:\(a(x, y) = a(x + 1, y) = \cdots = a(k, y) = 0\),而 \(a(k + 1, y) = 1\),我们认为 \(a(n + 1, y) = 1\)

预处理 \(h(x, y) = \sum \limits _{t = x} ^ k r(t, y)\),当 \(a(x, y) = 1\) 时,\(h(x, y) = 0\),否则 \(h(x, y) = h(x +1, y) + r(x, y) \times (k - t)\)。边界是 \(h(n +1, y) = 0\)

这样以来,当 \(a(x, y) = 0\)\(a(x + 1, y) = 0\) 时,\((x, y)\) 对应的 \(\texttt{F-}\) 数量就为 \(r(x, y) \times h(x + 2, y)\),否则显然为 \(0\)

时间复杂度 \(\Theta(nm)\)

/*
 * @Author: crab-in-the-northeast 
 * @Date: 2022-12-01 11:11:02 
 * @Last Modified by: crab-in-the-northeast
 * @Last Modified time: 2022-12-01 12:02:12
 */
#include <bits/stdc++.h>
#define int long long
inline int read() {
    int x = 0;
    bool f = true;
    char ch = getchar();
    for (; !isdigit(ch); ch = getchar())
        if (ch == '-')
            f = false;
    for (; isdigit(ch); ch = getchar())
        x = (x << 1) + (x << 3) + ch - '0';
    return f ? x : (~(x - 1));
}
inline std :: pair <std :: string, int> rest(bool space = true) {
    std :: string s;
    char ch = getchar();
    for (; !isgraph(ch); ch = getchar());
    for (; isgraph(ch); ch = getchar())
        s.push_back(ch);
    return {space ? " " + s : s, s.length()};
}

const int maxn = 1005;
const int maxm = 1005;
const int mod = 998244353;

bool a[maxn][maxm];
int r[maxn][maxm], g[maxn][maxm], h[maxn][maxm];

signed main() {
    int T = read(); read();
    while (T--) {
        std :: memset(r, -1, sizeof(r));
        std :: memset(g, 0, sizeof(g));
        std :: memset(h, 0, sizeof(h));

        int n = read(), m = read(), C = read(), F = read();

        for (int i = 1; i <= n; ++i) {
            std :: string s = rest().first;
            for (int j = 1; j <= m; ++j)
                a[i][j] = (s[j] == '1');
        }

        for (int i = 1; i <= n; ++i)
            for (int j = m; j; --j)
                r[i][j] = a[i][j] ? -1 : (r[i][j + 1] + 1);
        
        for (int i = n; i; --i)
            for (int j = 1; j <= m; ++j)
                g[i][j] = a[i][j] ? 0 : ((g[i + 1][j] + r[i][j]) % mod);

        for (int j = 1; j <= m; ++j) {
            for (int i = n, k = n; i; --i) {
                if (a[i][j])
                    k = i - 1;
                else
                    h[i][j] = (h[i + 1][j] + r[i][j] * (k - i)) % mod;
            }
        }
        
        int c = 0, f = 0;
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= m; ++j) {
                if (a[i][j] || a[i + 1][j])
                    continue;
                (c += r[i][j] * g[i + 2][j]) %= mod;
                (f += r[i][j] * h[i + 2][j]) %= mod;
            }
        }

        printf("%lld %lld\n", c * C, f * F);
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值