【NOIP2022】种花【计数,模拟】

[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 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

【数据范围】

对于所有数据,保证: 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

分析

题目描述比较复杂,其实就是形如“C”,“F”的花,其中“拐弯”那中间必须有一个空行。可见“C”形花可以看做“F”的特殊形式。先做两个前缀和记录一个点往右边和往下面有多少个点可以扩展。

然后有一种显然的做法:枚举每个点作为“C”的右上角,然后一个个枚举下去看看这个点往下有几个横出来的“分叉”,于是就可以计算,然后“F”的方案可以用“C”的方案乘上“尾巴”长度得到。这样子是 n 3 n^3 n3 的,也是我一年前赛场上的做法,69pts。

然后考虑怎么优化一下,能不能枚举下面的点的时候顺便计算了上面所有点与他形成的方案数?记录一个上面所有点的可向右扩展的和(用while判断有没有断开,断开就清零),然后每到一个地方就可以计算与上面所有点形成的方案数了,这样子变成 n 2 n^2 n2 .

上代码

69pts:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;

const int mod=998244353;
int T,id;
ll n,m,pc,pf,ansc,ansf;
int g[1010][1010];
ll l[1010][1010],c[1010][1010];

int main()
{
//	freopen("plant.in","r",stdin);
//	freopen("plant.out","w",stdout);
	scanf("%d%d",&T,&id);
	while(T--)
	{
		ansc=0;ansf=0;
		memset(g,0,sizeof(g));
		memset(l,0,sizeof(l));
		memset(c,0,sizeof(c));
		scanf("%lld%lld%lld%lld",&n,&m,&pc,&pf);
		if(pc==0&&pf==0)
		{
			cout<<0;
			continue;
		}
		for(int i=1;i<=n;i++) 
		{
			for(int j=1;j<=m;j++)
			{
				char ch;
				cin>>ch;
				g[i][j]=ch-'0';
			}
		}
		for(int i=1;i<=n;i++)
		{
			for(int j=m;j>=1;j--)
			{
				if(g[i][j]==1) l[i][j]=0;
				else l[i][j]=l[i][j+1]+1;
			}
		}
		for(int i=1;i<=m;i++)
		{
			for(int j=n;j>=1;j--)
			{
				if(g[j][i]==1) c[j][i]=0;
				else c[j][i]=c[j+1][i]+1;
			}
		}
		for(int i=1;i<=n-2;i++)
		{
			for(int j=i+2;j<=n;j++)
			{
				for(int k=1;k<=m;k++)
				{
					if(c[i][k]-c[j][k]!=j-i||c[i][k]==0||c[j][k]==0) continue;
					ansc=(ansc+((l[i][k]-1)*(l[j][k]-1)))%mod;
					ansf=ansf+((l[i][k]-1)*(l[j][k]-1)*(c[j][k]-1))%mod;
				}
			}
		}
		cout<<ansc*pc<<' '<<ansf*pf<<endl;
	}
	return 0;
 } 

AC:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;

const int mod=998244353;
int t,id,n,m;
ll cc,ff;
ll sum,ansc,ansf;
ll a[1010][1010],l[1010][1010],c[1010][1010];//右边 下面 

int main()
{
//	freopen("plant.in","r",stdin);
//	freopen("plant.out","w",stdout);
	scanf("%d%d",&t,&id);
	while(t--)
	{
		scanf("%d%d%lld%lld",&n,&m,&cc,&ff);
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=m;j++)
			{
				char t;
				cin>>t;
				a[i][j]=t-48;
			}
		}
	    for(int i=1;i<=m;i++)
	    {
	    	c[n+1][i]=-1;
	    	for(int j=n;j>=1;j--)
	    	{
	    		if(a[j][i]==0) c[j][i]=c[j+1][i]+1;
	    		else c[j][i]=-1;
			}
		}
		for(int i=1;i<=n;i++)
		{
			l[i][m+1]=-1;
			for(int j=m;j>=1;j--)
			{
				if(a[i][j]==0) l[i][j]=l[i][j+1]+1;
				else l[i][j]=-1;
			}
		}
        for(int i=1;i<=m;i++)
        {
        	int j=1;
        	ll sum=0;
        	while(j<=n)
        	{
        		if(a[j][i]==1||a[j-1][i]==1||a[j-2][i]==1) 
        		{
        			j+=1;
        			sum=0;
        			continue;
				}
				if(j-2<=0) 
				{
				    j++;
				    continue;
				}
				sum+=l[j-2][i];
				ansc=(ansc+l[j][i]*sum)%mod;
				ansf=(ansf+c[j][i]*l[j][i]*sum)%mod;
				j++;
			}
		}
		cout<<ansc*cc%mod<<' '<<ansf*ff%mod<<endl;
		ansc=ansf=0;
		memset(l,0,sizeof(l));
		memset(c,0,sizeof(c));
		memset(a,0,sizeof(a));
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值