NOIP2022T1题解

[NOIP2022] 种花(民间数据)

题目描述

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

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

一种种花方案被称为 C- 的,如果存在 x1,x2[1,n],以及 y0,y1,y2[1,m],满足 x1+1<x2,并且 y0<y1,y2m,使得第 x1 的第 y0 到第 y1 、第 x2 的第 y0 到第 y2 以及第 y0 的第 x1 到第 x2 不为土坑,且只在上述这些位置上种花。

一种种花方案被称为 F- 的,如果存在 x1,x2,x3[1,n],以及 y0,y1,y2[1,m],满足 x1+1<x2<x3,并且 y0<y1,y2m,使得第 x1 的第 y0 到第 y1 、第 x2 的第 y0 到第 y2 以及第 y0 的第 x1 到第 x3 不为土坑,且只在上述这些位置上种花。

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

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

对于所有数据,保证:1T51n,m1030c,f1ai,j{0,1}

题解

观察数据范围,考虑O(nm)求解,我们发现,F型就是C型扩展,考虑先求解C

因为C型涉及到了三段0,并且一个足够大的C能够通过上下两段的收缩来得到其他的合法方案,考虑对于枚举左上角,求解出合法的C

可以这样考虑,对于每一个左上角,需要往右和往下伸展,那么不妨用O(nm)的小DP求解出每一个点往右和往下最多伸展多少。由于这个C要求段的长度不能小于2,而一个C型如果确定了竖着的一段,那么横着的那两段,设最长为x1,x2那么每一段都可以从2取到最大值构成合法的C,总方案数就是(x11)(x21),考虑直接在这个长度减一

S1x,y,S2x,y分别表示点(x,y)向左和向下最长段的长度1,可以采用如下DP

	//预处理S1
		for(int y=1;y<=m;y++){
			for(int x=n;x;--x){
				if(a[x][y]==1)s2[x][y]=-1;
				else s2[x][y]=s2[x+1][y]+1;
			}
		}
	//预处理S2
		for(int x=1;x<=n;x++){
			for(int y=m;y;--y){
				if(a[x][y]==1)s1[x][y]=-1;
				else s1[x][y]=s1[x][y+1]+1;
			}
		} 

那么考虑如何才能对一个点求出所有合法的C,容易想到,总方案数中的x1是确定的,我们可以将所有的x2加上,很简单,再搞一个前缀和即可,设这个前缀和为S3

接着扩展到FF本质上是对C进行的扩展,可以求出一个C最多向下扩展多少,这就是这个C能够向下组合出F的总方案数,可以进行维护,就是再做一个前缀和S4S4x,y即为对于(x,y)向下扩展的每一个数的S2,S1的乘积,这样最终的答案即为

AnsC=x=1ny=1m[a[x,y]1]S1[x,y]×S3[x+2,y]

AnsF=x=1ny=1m[a[x,y]1]S1[x,y]×S4[x+2,y]

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define N 1050
#define p 998244353
int n,m,f1,f2,a[N][N],s1[N][N],s2[N][N],s3[N][N],s4[N][N],T,id;
int main(){
	ios::sync_with_stdio(false);
	cin>>T>>id;
	while(T--){
		memset(s1,-1,sizeof s1);
		memset(s2,-1,sizeof s2);
		memset(s3,0,sizeof s3);
		memset(s4,0,sizeof s4);
		cin>>n>>m>>f1>>f2;
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				char x;
				cin>>x;
				a[i][j]=x-'0';
			}
		}
	//预处理S1
		for(int y=1;y<=m;y++){
			for(int x=n;x;--x){
				if(a[x][y]==1)s2[x][y]=-1;
				else s2[x][y]=s2[x+1][y]+1;
			}
		}
	//预处理S2
		for(int x=1;x<=n;x++){
			for(int y=m;y;--y){
				if(a[x][y]==1)s1[x][y]=-1;
				else s1[x][y]=s1[x][y+1]+1;
			}
		} 
	//预处理S3
		for(int y=1;y<=m;y++){
			for(int x=n;x;--x){
				if(a[x][y]==1)s3[x][y]=0;
				else s3[x][y]=s3[x+1][y]+s1[x][y];
			}
		}
	//预处理S4
		for(int y=1;y<=m;y++){
			for(int x=n;x;--x){
				if(a[x][y]==1)s4[x][y]=0;
				else s4[x][y]=(1ll*s4[x+1][y]+1ll*s1[x][y]*s2[x][y])%p;
			}
		} 
	//
		int ans1=0,ans2=0;
		for(int x=1;x<=n-2;x++){
			for(int y=1;y<=m;y++){
				if(a[x][y]==1)continue;
				ans1=(1ll*ans1+1ll*s1[x][y]*s3[x+2][y]*(a[x+1][y]!=1)%p)%p;
				ans2=(1ll*ans2+1ll*s1[x][y]*s4[x+2][y]*(a[x+1][y]!=1)%p)%p;
			}
		}
		cout<<1ll*ans1*f1%p<<" "<<1ll*ans2*f2%p<<endl;
	}
} 
posted @   spdarkle  阅读(254)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示