[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\) 取模的结果即可,具体输出结果请看输出格式部分。

输入格式

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

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

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

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

输出格式

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

样例 #1

样例输入 #1

1 0
4 3 1 1
001
010
000
000

样例输出 #1

4 2

提示

【样例 1 解释】

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

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

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

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

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

【样例 2】

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

【样例 3】

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

【数据范围】

对于所有数据,保证:\(1 \leq T \leq 5\)\(1 \leq n, m \leq 10^3\)\(0 \leq c, f \leq 1\)\(a_{i,j} \in \{0, 1\}\)

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

特殊性质 A:\(\forall 1 \leq i \leq n, 1 \leq j \leq \left\lfloor \frac{m}{3} \right\rfloor\)\(a_{i, 3 j} = 1\)

特殊性质 B:\(\forall 1 \leq i \leq \left\lfloor \frac{n}{4} \right\rfloor, 1 \leq j \leq m\)\(a_{4 i, j} = 1\)

题解

这个题是独立做出来的绿题,非常开心
我们来看C的方案,观察样例就知道,对于C的两个顶点,我们需要知道每个顶点右面有几个零,如果知道这点,我们只需要使用乘法原理即可
对于(i,j)这个格子,我们怎么统计他右面有几个零?
刚开始我的方法是暴力维护,暴力维护的时间复杂度太高,我一直T的原因就在这里,后面我进行了修改,倒序枚举,从第m列开始,如果这个格子的数字是0,那么xh[i][j]=xh[i][j+1]+1,否则xh[i][j]=0,当然,我们也需要统计向上有多少个?方法是同样的。

那么我们怎么找C的方案数?我们发现在竖的方向上,至少存在3个连续的0,同时他们右面有多个0,我们是可以有C方案的,可以暴力统计,但是我发现,这个可以递推,用lst[i][j]记录在连续的0里面,目前匹配的数量是多少?如果当然(i,j)连续0的数量符合条件,那么前lst[i-2][j]是符合条件的,也就是说,和(i-2,j)匹配的,一定和他匹配,同时需要加上(i-2,j) 的向右数量,否则,匹配数量为0,同时更新lst[i][j],如何更新lst[i][j]呢?lst[i][j]默认等于lst[i-1][j],如果他自己也能匹配,加上自己向右的数量即可

经过上述过程,记录到pp[i][j]表示前面有多少个匹配数量,直接和0的数量相乘即可,那么C的问题就解决了

C的问题解决了之后,F的问题也就好解决了,因为F正好是C加上1个0,这个想法也是我偶然想到的,不得不说,这题出的实在是妙,我们记录C方案的前缀和即可

反思

对于我来说,最难的部分是对于(i,j)找匹配的问题,考虑到会出现如下的情况
0000
01
01
00000
那么我记录了一下上次的匹配好的位置,如果这次能匹配,那么递推一下,把上次匹配的数量加上他右面的连续0的数量,如果上次的位置和i的距离超过2,那么按照上述的方法来实现,如果遇到这种情况,上次匹配的位置是上一个,那么把前一个的匹配直接加上(i-2,j)的向右的数量
00000
0000
000
00
同时,我如何记录上一个位置呢?以列尾单位,用lst[j]记录了前一个位置,如果等于1,那么lst[j]归零,

点击查看代码
lst[1]=0;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                if(s[i][j]=='0'){
                    if(xs[i][j]>=3&&xh[i][j]>=2){
                        if(i-lst[j]>=2) pp[i][j]=pp[lst[j]][j]+xh[lst[j]][j]-1;
                        else {
                            int d=lst[j];
                            pp[i][j]=pp[lst[j]][j];
                            if(d<1) continue;
                            if(xh[d-1][j]>=2) pp[i][j]+=xh[d-1][j]-1;
                        }
                    }
                    if(xh[i][j]>=2) lst[j]=i;
                }else{
                    lst[j]=0;
                }
                
            }
        }
这样的实现方法是错误的,为什么?例如下面的例子 00000 0000 01 0000 lst是第2行,匹配数量等于2行的匹配,但是第1行也可以匹配,算法做不到 0000 所以这么写是错误的 ## 代码实现
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=1005;
int n,m,c,ff,id,t;
char s[maxn][maxn];
int xh[maxn][maxn],f[maxn][maxn],xs[maxn][maxn],pp[maxn][maxn],lst[maxn][maxn],sum[maxn][maxn];
long long ansc,ansf;
int main(){
    //freopen("plant4.in","r",stdin);
    scanf("%d%d",&t,&id);
    for(int ss=1;ss<=t;ss++){
        scanf("%d%d%d%d",&n,&m,&c,&ff);
        memset(xh,0,sizeof xh);
        memset(xs,0,sizeof xs);
        memset(f,0,sizeof f);
        memset(lst,0,sizeof lst);
        memset(pp,0,sizeof pp);
        memset(sum,0,sizeof sum);
        ansc=ansf=0;
        for(int i=1;i<=n;i++){
            scanf("%s",s[i]+1);
            if(s[i][m]=='0') xh[i][m]=1;
            else xh[i][m]=0;
            for(int j=m-1;j>=1;j--)
                if(s[i][j]=='0'){
                    xh[i][j]=xh[i][j+1]+1;
                    xs[i][j]=xs[i-1][j]+1;
                }
                else{
                    xh[i][j]=0;
                    xs[i][j]=0;
                }
        }
//        for(int i=1;i<=n;i++){
//            for(int j=1;j<=m;j++)
//                cout<<xs[i][j]<<" ";
//            cout<<endl;
//        }
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                if(s[i][j]=='0'){
                    if(xs[i][j]>=3){
                        if(xh[i][j]>=2) pp[i][j]=lst[i-2][j];
                        else pp[i][j]=0;
                    }
                    if(xh[i][j]>=2) lst[i][j]=lst[i-1][j]+xh[i][j]-1;
                    else lst[i][j]=lst[i-1][j];
                }else{
                    lst[i][j]=0;
                }
                
            }
        }
        for(int i=3;i<=n;i++){
            for(int j=1;j<=m;j++){
                if(s[i][j]=='0'&&xh[i][j]>=2&&xs[i][j]>=3){
                    f[i][j]=(xh[i][j]-1)*pp[i][j]%998244353;
                }
            }
        }
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++){
                if(s[i][j]=='0') {
                    ansf=(ansf+sum[i-1][j])%998244353;
                    sum[i][j]=(sum[i-1][j]+f[i][j])%998244353;
                    
                }
                else sum[i][j]=0;
            }
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                ansc=(ansc+f[i][j])%998244353;
        
        ansc=ansc*c%998244353;
        ansf=ansf*ff%998244353;
        printf("%lld %lld\n",ansc,ansf);
    }
}
/**
1 7
10 10 1 1
0000000000
0000000100
0000100000
0000000000
0000000000
0000000000
0000000001
0000000000
0010000001
0000000000
 */
posted @ 2024-01-24 11:38  xinyimama  阅读(77)  评论(0编辑  收藏  举报