Luogu-P8865-种花

题目 P8865 [NOIP2022] 种花

注:此后不提供题面。

思路:前缀和

严格讲也许是叫后缀和~

C型图案的产生

产生条件

首先,C型图案都需要什么条件才能出现?

  • 上层宽度大于 11
  • 中间 nn 层连续
  • 下层宽度大于 11

所以,对这三个条件依次处理。

上层宽度

建立一个前缀和数组 maxw[i][j]maxw[i][j],表示 (i,j)(i,j) 向右最多有几格连续的地面(不是坑)。

由于宽度要大于 11,所以当右边相邻的格子为坑时,maxwmaxw 也为 00

1
2
3
4
5
6
for(int i = n; i >= 1; i--) {
for(int j = m - 1; j >= 1; j--) {
if(hole[i][j] || hole[i][j + 1]) continue;
maxw[i][j] = maxw[i][j + 1] + 1;
}
}

乘法原理

思考:如果上层宽度为 aa,下层宽度为 bb,那总方案数为 aba \cdot b

同时,可以有多个上层和下层拼出C型图案。

所以,如果第 ii 层宽度为 xx,前面几层(不含相邻,空出来一行)的总宽度为 yy,那么这一层能产生的方案数为 xyx \cdot y

下层宽度

首先,既然要对每一层查找,就应该外层循环宽度,内层循环高度。

接下来,用 lstlst 保存前面几层的总宽度,那么就需要在本层循环结束后再将上一层的宽度合并到 lstlst,理由同上。

所以,到 (i,j)(i,j) 时,C型方案的数量就为:maxw[i][j]lstmaxw[i][j] \cdot lst

连续层

如果碰到了坑,直接跳过即可,并将 lstlst 重置为 00

F型图案的产生

相似的产生条件

不难发现,F型图案就是在C型图案的基础上加上一个向下的部分。

所以如果找到了C型图案,就可以判断是否能生成F型图案。

和C型图案类似,以下是产生F型图案的条件:

  • 上层宽度大于 11
  • 中间 nn 层连续
  • 下层宽度大于 11
  • 最下面延伸出去

以上三点直接与C型图案合并即可,我们重点关注最后一条。

向下延伸

使用一个前缀和数组 maxh[i][j]maxh[i][j] 表示向下延伸出去的最大高度。

1
2
3
4
5
6
for(int i = n; i >= 1; i--) {
for(int j = m - 1; j >= 1; j--) {
if(hole[i][j]) continue;
maxh[i][j] = maxh[i + 1][j] + 1;
}
}

计数

如果找到了能产生C型图案的位置,也要判断能否产生F型图案。

根据上面的结论,F型图案的方案数为:maxw[i][j]lstmaxh[i+1][j]maxw[i][j] \cdot lst \cdot maxh[i+1][j]

只需要循环遍历每一个点,将结果相加即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int ccnt = 0, fcnt = 0;
for(int j = 1; j <= m; j++) {
int lst = 0;
for(int i = 1; i <= n; i++) {
// printf("(%d, %d): %d\n", i, j, maxw[i - 1][j]);
if(maxw[i][j]) {
const int res = lst * maxw[i][j];
ccnt += res;
fcnt += 1ll * res * maxh[i + 1][j];
}
if(hole[i][j])
lst = 0;
else {
lst += maxw[i - 1][j];
}
}
}

注意取模!

AC Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
// template v12
#include <bits/stdc++.h>
#define set(x, y) memset(x, y, sizeof x)

using namespace std;
using ll = long long;

const int inf = 0x7f7f7f7f;

const int N = 1000 + 10;
const int MOD = 998244353;

bool hole[N][N];

int maxw[N][N], maxh[N][N];

void init(){
set(hole, 0);
set(maxw, 0);
set(maxh, 0);
}

int main() {
#ifndef ONLINE_JUDGE
freopen("plant1.in", "r", stdin);
freopen("plant.out", "w", stdout);
#endif
int T, id;
cin >> T >> id;
int n, m, c, f;
while(T-- > 0) {
init();
cin >> n >> m >> c >> f;
char ch;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
cin >> ch;
hole[i][j] = ch == '1';
}
}
for(int i = n; i >= 1; i--) {
for(int j = m - 1; j >= 1; j--) {
if(hole[i][j] || hole[i][j + 1]) continue;
maxw[i][j] = maxw[i][j + 1] + 1;
}
for(int j = m - 1; j >= 1; j--) {
if(hole[i][j]) continue;
maxh[i][j] = maxh[i + 1][j] + 1;
}
}
int ccnt = 0, fcnt = 0;
for(int j = 1; j <= m; j++) {
int lst = 0;
for(int i = 1; i <= n; i++) {
// printf("(%d, %d): %d\n", i, j, maxw[i - 1][j]);
if(maxw[i][j]) {
const int res = lst * maxw[i][j] % MOD;
ccnt += res;
fcnt += (1ll * res * maxh[i + 1][j]) % MOD;
}
if(hole[i][j])
lst = 0;
else {
lst += maxw[i - 1][j];
}

ccnt %= MOD;
fcnt %= MOD;
}
}
cout << c * ccnt << ' ' << f * fcnt << endl;
}
return 0;
}