P8865 [NOIP2022] 种花 题解

P8865 [NOIP2022] 种花

非常优质的签到题

题面:

题目描述

小 C 决定在他的花园里种出 CCF \texttt{CCF} CCF 字样的图案,因此他想知道 C \texttt C C F \texttt F F 两个字母各自有多少种种花的方案;不幸的是,花园中有一些土坑,这些位置无法种花,因此他希望你能帮助他解决这个问题。

花园可以看作有 n × m n\times m n×m 个位置的网格图,从上到下分别为第 1 1 1 到第 n n n 行,从左到右分别为第 1 1 1 列到第 m m m 列,其中每个位置有可能是土坑,也有可能不是,可以用 a i , j = 1 a_{i,j} = 1 ai,j=1 表示第 i i i 行第 j j j 列这个位置有土坑,否则用 a i , j = 0 a_{i,j} = 0 ai,j=0 表示这个位置没土坑。

一种种花方案被称为 C- \texttt{C-} C- 的,如果存在 x 1 , x 2 ∈ [ 1 , n ] x_1, x_2 \in [1, n] x1,x2[1,n],以及 y 0 , y 1 , y 2 ∈ [ 1 , m ] y_0, y_1, y_2 \in [1, m] y0,y1,y2[1,m],满足 x 1 + 1 < x 2 x_1 + 1 < x_2 x1+1<x2,并且 y 0 < y 1 , y 2 ≤ m y_0 < y_1, y_2 \leq m y0<y1,y2m,使得第 x 1 x_1 x1 的第 y 0 y_0 y0 到第 y 1 y_1 y1 、第 x 2 x_2 x2 的第 y 0 y_0 y0 到第 y 2 y_2 y2 以及第 y 0 y_0 y0 的第 x 1 x_1 x1 到第 x 2 x_2 x2 不为土坑,且只在上述这些位置上种花。

一种种花方案被称为 F- \texttt{F-} F- 的,如果存在 x 1 , x 2 , x 3 ∈ [ 1 , n ] x_1, x_2, x_3 \in [1, n] x1,x2,x3[1,n],以及 y 0 , y 1 , y 2 ∈ [ 1 , m ] y_0, y_1, y_2 \in [1, m] y0,y1,y2[1,m],满足 x 1 + 1 < x 2 < x 3 x_1 + 1 < x_2 < x_3 x1+1<x2<x3,并且 y 0 < y 1 , y 2 ≤ m y_0 < y_1, y_2 \leq m y0<y1,y2m,使得第 x 1 x_1 x1 的第 y 0 y_0 y0 到第 y 1 y_1 y1 、第 x 2 x_2 x2 的第 y 0 y_0 y0 到第 y 2 y_2 y2 以及第 y 0 y_0 y0 的第 x 1 x_1 x1 到第 x 3 x_3 x3 不为土坑,且只在上述这些位置上种花。

样例一解释中给出了 C- \texttt{C-} C- 形和 F- \texttt{F-} F- 形种花方案的图案示例。

现在小 C 想知道,给定 n , m n, m n,m 以及表示每个位置是否为土坑的值 { a i , j } \{a_{i,j}\} {ai,j} C- \texttt{C-} C- 形和 F- \texttt{F-} F- 形种花方案分别有多少种可能?由于答案可能非常之大,你只需要输出其对 998244353 998244353 998244353 取模的结果即可,具体输出结果请看输出格式部分。

输入格式

第一行包含两个整数 T , i d T, id T,id,分别表示数据组数和测试点编号。如果数据为样例则保证 i d = 0 id = 0 id=0

接下来一共 T T T 组数据,在每组数据中:

第一行包含四个整数 n , m , c , f n, m, c, f n,m,c,f,其中 n , m n, m n,m 分别表示花园的行数、列数,对于 c , f c, f c,f 的含义见输出格式部分。

接下来 n n n 行,每行包含一个长度为 m m m 且仅包含 0 0 0 1 1 1 的字符串,其中第 i i i 个串的第 j j j 个字符表示 a i , j a_{i,j} ai,j,即花园里的第 i i i 行第 j j j 列是不是一个土坑。

输出格式

设花园中 C- \texttt{C-} C- 形和 F- \texttt{F-} F- 形的种花方案分别有 V C V_C VC V F V_F VF 种,则你需要对每一组数据输出一行用一个空格隔开的两个非负整数,分别表示 ( c × V C )   m o d   998244353 (c \times V_C) \bmod 998244353 (c×VC)mod998244353 ( f × V F )   m o d   998244353 (f \times V_F ) \bmod 998244353 (f×VF)mod998244353 ,其中 a   m o d   P a \bmod P amodP 表示 a a a P P P 取模后的结果。

样例 #1
样例输入 #1
1 0
4 3 1 1
001
010
000
000
样例输出 #1
4 2
提示

【样例 1 解释】

四个 C- \texttt{C-} C- 形种花方案为:

**1 **1 **1 **1
*10 *10 *10 *10
**0 *** *00 *00
000 000 **0 ***

其中 * \texttt* * 表示在这个位置种花。注意 C \texttt C C 的两横可以不一样长。

类似的,两个 F- \texttt{F-} F- 形种花方案为:

**1 **1
*10 *10
**0 ***
*00 *00

【样例 2】

见附件下的 plant/plant2.in \texttt{plant/plant2.in} plant/plant2.in plant/plant2.ans \texttt{plant/plant2.ans} plant/plant2.ans

【样例 3】

见附件下的 plant/plant3.in \texttt{plant/plant3.in} plant/plant3.in plant/plant3.ans \texttt{plant/plant3.ans} plant/plant3.ans

【数据范围】

对于所有数据,保证: 1 ≤ T ≤ 5 1 \leq T \leq 5 1T5 1 ≤ n , m ≤ 1 0 3 1 \leq n, m \leq 10^3 1n,m103 0 ≤ c , f ≤ 1 0 \leq c, f \leq 1 0c,f1 a i , j ∈ { 0 , 1 } a_{i,j} \in \{0, 1\} ai,j{0,1}

测试点编号 n n n m m m c = c= c= f = f= f=特殊性质测试点分值
1 1 1 ≤ 1000 \leq 1000 1000 ≤ 1000 \leq 1000 1000 0 0 0 0 0 0 1 1 1
2 2 2 = 3 =3 =3 = 2 =2 =2 1 1 1 1 1 1 2 2 2
3 3 3 = 4 =4 =4 = 2 =2 =2 1 1 1 1 1 1 3 3 3
4 4 4 ≤ 1000 \leq 1000 1000 = 2 =2 =2 1 1 1 1 1 1 4 4 4
5 5 5 ≤ 1000 \leq 1000 1000 ≤ 1000 \leq 1000 1000 1 1 1 1 1 1A 4 4 4
6 6 6 ≤ 1000 \leq 1000 1000 ≤ 1000 \leq 1000 1000 1 1 1 1 1 1B 6 6 6
7 7 7 ≤ 10 \leq 10 10 ≤ 10 \leq 10 10 1 1 1 1 1 1 10 10 10
8 8 8 ≤ 20 \leq 20 20 ≤ 20 \leq 20 20 1 1 1 1 1 1 6 6 6
9 9 9 ≤ 30 \leq 30 30 ≤ 30 \leq 30 30 1 1 1 1 1 1 6 6 6
10 10 10 ≤ 50 \leq 50 50 ≤ 50 \leq 50 50 1 1 1 1 1 1 8 8 8
11 11 11 ≤ 100 \leq 100 100 ≤ 100 \leq 100 100 1 1 1 1 1 1 10 10 10
12 12 12 ≤ 200 \leq 200 200 ≤ 200 \leq 200 200 1 1 1 1 1 1 6 6 6
13 13 13 ≤ 300 \leq 300 300 ≤ 300 \leq 300 300 1 1 1 1 1 1 6 6 6
14 14 14 ≤ 500 \leq 500 500 ≤ 500 \leq 500 500 1 1 1 1 1 1 8 8 8
15 15 15 ≤ 1000 \leq 1000 1000 ≤ 1000 \leq 1000 1000 1 1 1 0 0 0 6 6 6
16 16 16 ≤ 1000 \leq 1000 1000 ≤ 1000 \leq 1000 1000 1 1 1 1 1 1 14 14 14

特殊性质 A: ∀ 1 ≤ i ≤ n , 1 ≤ j ≤ ⌊ m 3 ⌋ \forall 1 \leq i \leq n, 1 \leq j \leq \left\lfloor \frac{m}{3} \right\rfloor ∀1in,1j3m a i , 3 j = 1 a_{i, 3 j} = 1 ai,3j=1

特殊性质 B: ∀ 1 ≤ i ≤ ⌊ n 4 ⌋ , 1 ≤ j ≤ m \forall 1 \leq i \leq \left\lfloor \frac{n}{4} \right\rfloor, 1 \leq j \leq m ∀1i4n,1jm a 4 i , j = 1 a_{4 i, j} = 1 a4i,j=1

简单计数题:

先看到数据规模: n ≤ 1000 , m ≤ 1000 , T ≤ 5 n \leq 1000,m \leq 1000,T \leq 5 n1000,m1000,T5,告诉我们复杂度大概是 O ( T n m ) \mathcal{O}(Tnm) O(Tnm)

先考虑 C C C 形,

我们可以确定 C C C 形的左上角 O ( n m ) \mathcal{O}(nm) O(nm)

然后,我们的目标是:

  1. 求出 C C C 第一行长度的可能结果数
  2. 求出 C C C 第一列的可取长度的范围
  3. 求出 C C C 每一列长度对应的行的可能结果数
  4. 目标 1 1 1 和 目标 3 3 3 相乘累加到答案中

以上的步骤全部需要 O ( 1 ) \mathcal{O}(1) O(1) 完成,所以我们考虑预处理

对于 目标 1 1 1,我们可以预处理出 位置 ( i , j ) (i,j) (i,j) 离右手边最近的障碍的距离。

对于 目标 3 3 3,一个个算肯定是 武则天丧夫 没理智的,但是我们求的是一个区间结果,这个时候就该考虑前缀和

我们用新的数组存储(我们预处理的右手边最近障碍距离)的前缀和,在执行目标 3 3 3 时用前缀和作差求出结果。

但是 是多少?

我们可以再预处理 位置 ( i , j ) (i,j) (i,j) 离正下方最近障碍的距离。那么我们询问的区间就是 ( x + 2 , j ) ∼ ( x + d i s ( x ) , j ) (x + 2,j) \sim (x + dis(x),j) (x+2,j)(x+dis(x),j)

自此 C C C 形就解决了!

再考虑 F F F 形:

ta 和 C C C 有什么区别呢?不就是第一列可以有个小尾巴嘛!

如果我们已经确定了 F F F 形的第二 ,那么可以得到累加多少结果呢?

很显然是 小尾巴 的长度。那么我们对于每一个可能的横都可以预处理出带上小尾巴的结果数,

然后还是熟悉的区间结果查询,我们依旧对上述结果做前缀和。

注意多测!清空 + 换行!

AC-code:

#include<bits/stdc++.h>
using namespace std;
#define int long long		
int rd() {
	int x = 0, w = 1;
	char ch = 0;
	while (ch < '0' || ch > '9') {
		if (ch == '-') w = -1;
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = x * 10 + (ch - '0');
		ch = getchar();
	}
	return x * w;
}

void wt(int x) {
	static int sta[35];
	int f = 1;
	if(x < 0) f = -1,x *= f;
	int top = 0;
	do {
		sta[top++] = x % 10, x /= 10;
	} while (x);
	if(f == -1) putchar('-');
	while (top) putchar(sta[--top] + 48);
}
int T,id;

namespace workspace{
const int N = 1005,mod = 998244353;
int n,m,c,f;
char d[N];
int s[N][N],h[N][N];
int ss[N][N],_s[N][N];

void init() {
	memset(s,0,sizeof(s));
	memset(ss,0,sizeof(ss));
	memset(h,0,sizeof(h));
	memset(_s,0,sizeof(_s));
}

void solve() {
	init();
	n = rd(),m = rd(),c = rd(),f = rd();
	for(int i = 1;i<=n;i++) {
		scanf("%s",d + 1);
		for(int j = 1;j<=m;j++)
			s[i][j] = d[j] - '0';
	}
	for(int j = 1;j<=m;j++)
		for(int i = n,k = n + 1;i>=1;i--)
			if(s[i][j]) k = i,h[i][j] = k - i - 1;
			else h[i][j] = k - i - 1;
	for(int i = 1;i<=n;i++)
		for(int j = m,k = m + 1;j >= 1;j--)
			if(s[i][j]) k = j,s[i][j] = k - j - 1;
			else s[i][j] = k - j - 1;
	for(int j = 1;j<=m;j++) {
		for(int i = n;i>=1;i--){
			ss[i][j] = ss[i + 1][j] + s[i][j];
			_s[i][j] = s[i][j] * h[i][j];
			_s[i][j] = _s[i + 1][j] + _s[i][j];
		}
	}
	int C = 0,F = 0;
	for(int j = 1;j<=m;j++) {
		int k = 1,re = 0;
		while(k + 2 <= n) {
			while((s[k][j] == -1 || s[k + 1][j] == -1 || s[k + 2][j] == -1) && k <= n) k++;
			if(k >= n - 1) break;
			re = s[k][j];
			(C += re * (ss[k + 2][j] - ss[k + h[k][j] + 1][j])) %= mod;
			if(k + 3 <= n && ~s[k + 3][j]) 	
				(F += re * (_s[k + 2][j] - _s[k + h[k][j] + 1][j])) %= mod;
			k++;
		}
	}
	wt(c * C),putchar(' '),wt(f * F);
	putchar('\n');
}		
}
signed main() {
	T = rd(),rd();
	while(T--) workspace::solve();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值