[NOIP2022] 种花(民间数据)
题目描述
小 C 决定在他的花园里种出 \(\texttt{CCF}\) 字样的图案,因此他想知道 \(\texttt C\) 和 \(\texttt F\) 两个字母各自有多少种种花的方案;不幸的是,花园中有一些土坑,这些位置无法种花,因此他希望你能帮助他解决这个问题。
花园可以看作有 \(n\times m\) 个位置的网格图,从上到下分别为第 \(1\) 到第 \(n\) 行,从左到右分别为第 \(1\) 列到第 \(m\) 列,其中每个位置有可能是土坑,也有可能不是,可以用 \(a_{i,j} = 1\) 表示第 \(i\) 行第 \(j\) 列这个位置有土坑,否则用 \(a_{i,j} = 0\) 表示这个位置没土坑。
一种种花方案被称为 \(\texttt{C-}\) 形的,如果存在 \(x_1, x_2 \in [1, n]\),以及 \(y_0, y_1, y_2 \in [1, m]\),满足 \(x_1 + 1 < x_2\),并且 \(y_0 < y_1, y_2 \leq m\),使得第 \(x_1\) 行的第 \(y_0\) 到第 \(y_1\) 列、第 \(x_2\) 行的第 \(y_0\) 到第 \(y_2\) 列以及第 \(y_0\) 列的第 \(x_1\) 到第 \(x_2\) 行都不为土坑,且只在上述这些位置上种花。
一种种花方案被称为 \(\texttt{F-}\) 形的,如果存在 \(x_1, x_2, x_3 \in [1, n]\),以及 \(y_0, y_1, y_2 \in [1, m]\),满足 \(x_1 + 1 < x_2 < x_3\),并且 \(y_0 < y_1, y_2 \leq m\),使得第 \(x_1\) 行的第 \(y_0\) 到第 \(y_1\) 列、第 \(x_2\) 行的第 \(y_0\) 到第 \(y_2\) 列以及第 \(y_0\) 列的第 \(x_1\) 到第 \(x_3\) 行都不为土坑,且只在上述这些位置上种花。
样例一解释中给出了 \(\texttt{C-}\) 形和 \(\texttt{F-}\) 形种花方案的图案示例。
现在小 C 想知道,给定 \(n, m\) 以及表示每个位置是否为土坑的值 \(\{a_{i,j}\}\),\(\texttt{C-}\) 形和 \(\texttt{F-}\) 形种花方案分别有多少种可能?由于答案可能非常之大,你只需要输出其对 \(998244353\) 取模的结果即可,具体输出结果请看输出格式部分。
对于所有数据,保证:\(1 \leq T \leq 5\),\(1 \leq n, m \leq 10^3\),\(0 \leq c, f \leq 1\),\(a_{i,j} \in \{0, 1\}\)。
题解
观察数据范围,考虑\(O(nm)\)求解,我们发现,\(F\)型就是\(C\)型扩展,考虑先求解\(C\)型
因为\(C\)型涉及到了三段\(0\),并且一个足够大的\(C\)能够通过上下两段的收缩来得到其他的合法方案,考虑对于枚举左上角,求解出合法的\(C\)
可以这样考虑,对于每一个左上角,需要往右和往下伸展,那么不妨用\(O(nm)\)的小\(DP\)求解出每一个点往右和往下最多伸展多少。由于这个\(C\)要求段的长度不能小于\(2\),而一个\(C\)型如果确定了竖着的一段,那么横着的那两段,设最长为\(x_1,x_2\)那么每一段都可以从\(2\)取到最大值构成合法的\(C\),总方案数就是\((x_1-1)(x_2-1)\),考虑直接在这个长度减一
设\(S1_{x,y},S2_{x,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\),容易想到,总方案数中的\(x_1\)是确定的,我们可以将所有的\(x_2\)加上,很简单,再搞一个前缀和即可,设这个前缀和为\(S3\)
接着扩展到\(F\),\(F\)本质上是对\(C\)进行的扩展,可以求出一个\(C\)最多向下扩展多少,这就是这个\(C\)能够向下组合出\(F\)的总方案数,可以进行维护,就是再做一个前缀和\(S4\),\(S4_{x,y}\)即为对于\((x,y)\)向下扩展的每一个数的\(S2,S1\)的乘积,这样最终的答案即为
\[Ans_{C}=\sum_{x=1}^n\sum_{y=1}^m[a[x,y]\neq 1]S1[x,y]\times S3[x+2,y] \]\[Ans_{F}=\sum_{x=1}^n\sum_{y=1}^m[a[x,y]\neq 1]S1[x,y]\times 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;
}
}
标签:int,题解,S1,texttt,土坑,leq,种花,NOIP2022T1
From: https://www.cnblogs.com/oierpyt/p/16940094.html