题面解释

给定\(n\times m\)大小的花园,有一些不能种花的土坑,现给定所有格的状态,求在花园里种出"C"形和"F"形的方案数。 "C"形:

1
2
3
4
**
*
**
//至少宽两格高三格

"F"形:

1
2
3
4
5
**
*
**
*
//至少宽两格高四格

解法

要保证不重不漏,我们枚举每个点为左上角,让该点所在行为"C"形上方的横,它的长度有若干种情况,对于\((i,j)\)这个点,我们设\(rt_{i,j}\),表示\((i,j)\)这个点到其右方最近土坑的距离,则该行的方案数为\(rt_{i,j}-1\)。递推式:

\((i,j)\)为土坑,\(rt_{i,j}=0\)

\((i,j)\)可种花,\(rt_{i,j}=rt_{i,j+1}+1\)

接着我们考虑"C"形下面一行,方案数为\(rt_{k,j}-1\),枚举从\(i+2\)行开始每一行,累加方案数,与第\(i\)行方案数相乘得答案,设\(ansc\)

\[ansc_{i,j}=(rt_{i,j}-1)\times \sum^{n}_{k=i+2}(rt_{k,j}-1)\]

无法向下延伸时break;

匚,受物之器。 同队大佬亅匚丫

观察到“F”形一竖长度也有若干种情况,考虑“C”形左上角,只需乘以“C”形下方横行方案数按照求\(ansc\)的方法累加相乘即可。

\(dw_{i,j}\)表示\((i,j)\)这个点到其下方最近土坑的距离,则以\((i,j)\)作为“C”形左上角向下延伸为“F”形的方案数为\(dw_{i,j}-1\)。递推式:

\((i,j)\)为土坑,\(dw_{i,j}=0\)

\((i,j)\)可种花,\(dw_{i,j}=dw_{i+1,j}+1\)

\(ansf\)

\[ansf_{i,j}=(rt_{i,j}-1)\times \sum^{i+dw_{i,j}-1}_{k=i+2}(rt_{k,j}-1)\times (dw_{k,j}-1)\]

所有点的方案数相加即答案。复杂度\(O(n^3)\),考场上能过!

显然的优化:求答案时加的一维枚举可以省去。

观察求\(ansc\)\(ansf\)的式子,它们都包含了从当前行的下方第二行到末尾行的方案数。考虑从下方行的答案递推上方行的答案。注意当前行的状态并不一定来自其下方第一行:可能有土坑,也可能它右边第一格为土坑导致无法延伸,该点答案为\(0\)

因此维护一个\(nxt_{i,j}\)表示能转移到\((i,j)\)的行数,\(nxt_{i,j}\)\(0\)表示无法转移,此时用上述方法暴力求算。

\((n-2)\)行到\(1\)行枚举行数:初始\(nxt_{i,j}=0\),成功转移后\(nxt_{i,j}=i\)表示它可用于转移上方状态。上方行被枚举到时如果为土坑将\(nxt_{i,j}\)重置为0,否则\(nxt_{i,j}=nxt_{i+1,j}\)

改写一下前面的递推式:

\[ansc_{i,j}=(rt_{i,j}-1)\times \frac {ansc_{nxt_{i,j},j}}{rt_{nxt_{i,j},j}-1}\]

\[ansf_{i,j}=(rt_{i,j}-1)\times \frac {ansf_{nxt_{i,j},j}}{rt_{nxt_{i,j},j}-1}\]

注意除改为乘逆元(打表打个10KB

但这个转移式是错误的。当从\((i,j)\)下方第二行及更远的地方转移时,\((nxt_{i,j},j)\)这行的方案数对于第\(i\)行是合法的,因此应加入。

在求\((nxt_{i,j},j)\)时我们忽略\(nxt_{i,j}+1\)这一行,但这一行对于上面所有行都是合法的。在\((nxt_{i,j},j)\)累加进答案后,我们将这一行加进来才可以正确转移。

正确的递推式:

\[ansc_{i,j}=(rt_{i,j}-1)\times (\frac {ansc_{nxt_{i,j},j}}{rt_{nxt_{i,j},j}-1}+[nxt_{i,j}\ne i+1]\times (rt_{nxt_{i,j}-1}))\]

\[ansf_{i,j}=(rt_{i,j}-1)\times (\frac {ansf_{nxt_{i,j},j}}{rt_{nxt_{i,j},j}-1}+[nxt_{i,j}\ne i+1]\times (rt_{nxt_{i,j}-1})\times (dw_{nxt_{i,j}-1}))\]

最后,

\[ansc_{i,j}=(rt_{i,j}-1)\times (rt_{i+1,j}-1)\]

\[ansf_{i,j}=(rt_{i,j}-1)\times (rt_{i+1,j}-1)\times (dw_{i+1,j}-1)\]

复杂度\(O(n^2)\)。贴上优雅的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#include <bits/stdc++.h>
using namespace std;
namespace Kochi
{
template<typename Kopi>
inline void read(Kopi& ret)
{
ret=0;
bool f=0;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-') f=1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
ret=(ret<<3)+(ret<<1)+(ch^48);
ch=getchar();
}
if(f) ret=-ret;
return;
}
const int maxn=1003,mod=998244353;
int rt[maxn][maxn];
int dw[maxn][maxn];
int nxt[maxn][maxn];
long long inv[maxn];
char gd[maxn][maxn];
long long ansc[maxn][maxn];
long long ansf[maxn][maxn];
long long ascc,asff;
int T,id;
int n,m,c,f;
int Ayatsuki()
{
read(T),read(id);
inv[1]=1;
for(int i=2;i<=1000;i++)
inv[i]=(mod-inv[mod%i]*(mod/i)%mod);
while(T--)
{
read(n),read(m),read(c),read(f);
if(c==0&&f==0)
{
printf("0 0\n");
continue;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
gd[i][j]=getchar();
while(gd[i][j]==' '||gd[i][j]=='\n')
gd[i][j]=getchar();
}
for(int i=n;i>=1;i--)
for(int j=m;j>=1;j--)
{
if(i==n)
dw[i][j]=(gd[i][j]=='0')?1:0;
else
dw[i][j]=(gd[i][j]=='1')?0:(dw[i+1][j]+1);
if(j==m)
rt[i][j]=(gd[i][j]=='0')?1:0;
else
rt[i][j]=(gd[i][j]=='1')?0:(rt[i][j+1]+1);
}
ascc=asff=0;
for(int i=n-2;i>=1;i--)
for(int j=1;j<=m-1;j++)
{
ansc[i][j]=ansf[i][j]=0;
if(i==n-2) nxt[i][j]=0;
else nxt[i][j]=nxt[i+1][j];
if(rt[i][j]<2||dw[i][j]<3)
{
if(dw[i][j]==0) nxt[i][j]=0;
continue;
}
if(!nxt[i][j])
{
for(int k=i+2;k<=n;k++)
{
if(gd[k][j]=='1') break;
ansc[i][j]=(ansc[i][j]+(rt[i][j]-1)*(rt[k][j]-1)%mod)%mod;
ansf[i][j]=(ansf[i][j]+(rt[i][j]-1)*(rt[k][j]-1)%mod*(dw[k][j]-1)%mod)%mod;
}
nxt[i][j]=i;
}
else
{
int k=nxt[i][j];
ansc[i][j]=(ansc[k][j]*inv[rt[k][j]-1]%mod+(k-i>1)*(rt[k][j]-1)%mod)*(rt[i][j]-1)%mod;
ansf[i][j]=(ansf[k][j]*inv[rt[k][j]-1]%mod+(k-i>1)*(rt[k][j]-1)*(dw[k][j]-1)%mod)*(rt[i][j]-1)%mod;
nxt[i][j]=i;
}
ascc=(ascc+ansc[i][j]%mod)%mod;
asff=(asff+ansf[i][j]%mod)%mod;
ansc[i][j]=(ansc[i][j]+(rt[i][j]-1)*(rt[i+1][j]-1)%mod)%mod;
ansf[i][j]=(ansf[i][j]+(rt[i][j]-1)*(rt[i+1][j]-1)%mod*(dw[i+1][j]-1)%mod)%mod;
}
printf("%lld %lld\n",c*ascc,f*asff);
}
return 0;
}
}
int main()
{
// freopen("plant.in","r",stdin);
// freopen("plant.out","w",stdout);
return Kochi::Ayatsuki();
}
//it's the only one chance
//now it's your turn,stickman!

如果要更简洁的解法,可以试试枚举左下角,复杂度相同但可能更快。这个交给你们自己实现。

Stickman的小窝 省一爷爷coding_hong