题解:「NOIP2022 提高组」种花

题解:「NOIP2022 提高组」种花

题目大意:给定一个 n×m 的01矩阵,0表示可以种花,1表示土坑(无法种花),现在要在图上种出一个C型或F型(C,F横着的两条线的长度都可以不同,但一定是面向右边的),现在问你种C和F分别有多少种方案(除了这个形状外不能在任何地方种花),多组数据,T5。 答案对998244353取模 。

原题面中对形状的定义是这样的:

一种种花方案被称为 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 不为土坑,且只在上述这些位置上种花。

Subtask:n500

先考虑C形,发现当两横线位置固定时,竖线位置是固定的。所以只需关心横线对答案的贡献

横线对答案的贡献等于两横线长度之积,怎么快速求出每个点往右能延伸的最大长度呢?每一行从右往左做后缀和即可,预处理可以在 O(N2) 内完成

考虑 O(N3) 统计答案:

第一层循环枚举列数 j

第二层枚举上方横线所在行号 i

第三层枚举下方横线所在行号 k

ri,j 表示点 (i,j) 能向右延伸的最大长度,则有

ansc=j=1mi=1nk=i+2n(ri,j1)×(rk,j1)

那F形呢?发现就是在C的基础上在竖线下方加一截。

ci,j 表示 (i,j) 能向下延伸的最大长度,同样可以预处理后缀和,则有

ansc=j=1mi=1nk=i+2n(ri,j1)×(rk,j1)×(ck,j1)

时间复杂度 O(TN3) ,加上输出0的特判,期望得分67pts,但好像CCF数据太水,T根本没有5,所以实际得分75pts。

正解: n1000

考虑怎么优化到 O(N2) 或者 O(N2logN)

发现固定 i,j 后, 涉及k 的部分可以进一步处理成前缀和。

所以记 sri,j 表示 k=inrk,j1ssri,j 表示 k=in(rk,j1)×(ck,j1)

注意做的也是后缀和而不是前缀

然后就只需要枚举 i,j 即可,时间复杂度 O(N2) , 期望得分100pts

#include<bits/stdc++.h>
#define F(i,l,r) for(int i=l;i<=r;++i)
#define G(i,r,l) for(int i=r;i>=l;--i)
#define mem(a) memset(a,0,sizeof(a))
#define ll long long
const int N=1010;
const ll mod=998244353;
int n,m,vc,vf,t,id;
char ch[N][N];
ll ansf,ansc,r[N][N],c[N][N],sr[N][N],ssr[N][N];
int main(){
//	freopen("plant.in","r",stdin);
//	freopen("plant.out","w",stdout);
	scanf("%d%d",&t,&id); 
	while(t--){
		ansf=0,ansc=0,mem(r),mem(c),mem(sr),mem(ssr),mem(ch);
		scanf("%d%d%d%d",&n,&m,&vc,&vf);
		F(i,1,n) scanf("%s",ch[i]+1);
		if(!vc && !vf){ puts("0 0"); continue; }
		F(i,1,n) G(j,m,1) ch[i][j]=='1'?r[i][j]=0:r[i][j]=r[i][j+1]+1;
		F(j,1,m) G(i,n,1) ch[i][j]=='1'?c[i][j]=0:c[i][j]=c[i+1][j]+1;
		F(j,1,m) G(i,n,1) {
			if(ch[i][j]=='1') continue;
			sr[i][j]=r[i][j]-1,ssr[i][j]=(r[i][j]-1)*(c[i][j]-1)%mod;
			if(ch[i+1][j]=='0' && i+1<=n) sr[i][j]=(sr[i][j]+sr[i+1][j])%mod,ssr[i][j]=(ssr[i][j]+ssr[i+1][j])%mod;
		}
		F(j,1,m){
			F(i,1,n-2){
				if(r[i][j]<=1 || ch[i+1][j]=='1' || ch[i+2][j]=='1') continue;
				ansc=(ansc+(r[i][j]-1)*sr[i+2][j]%mod)%mod;
				ansf=(ansf+(r[i][j]-1)*ssr[i+2][j]%mod)%mod;
			}
		}
		printf("%lld %lld\n",vc?ansc:0,vf?ansf:0);
	}
	return 0;
}
posted @   superl61  阅读(197)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示