[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,y2≤m,使得第 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,y2≤m,使得第 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 1≤T≤5, 1 ≤ n , m ≤ 1 0 3 1 \leq n, m \leq 10^3 1≤n,m≤103, 0 ≤ c , f ≤ 1 0 \leq c, f \leq 1 0≤c,f≤1, 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 1 | A | 4 4 4 |
6 6 6 | ≤ 1000 \leq 1000 ≤1000 | ≤ 1000 \leq 1000 ≤1000 | 1 1 1 | 1 1 1 | B | 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 ∀1≤i≤n,1≤j≤⌊3m⌋, 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 ∀1≤i≤⌊4n⌋,1≤j≤m, 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;
}