Description
Farmer John 有一块小的田地,形状为一个 (N) 行 (N) 列的一个方阵,对于所有的 (1 le i,j le N),从上往下的第 (i) 行的从左往右第 (j) 个方格记为 ((i,j))。他有兴趣在他的田地里种植甜玉米和苜蓿。为此,他需要安装一些特殊的洒水器。
在方格 ((I,J)) 中的甜玉米洒水器可以喷洒到所有左下方的方格:即满足 (I le i) 以及 (j le J) 的 ((i,j))。
在方格 ((I,J)) 中的苜蓿洒水器可以喷洒到所有右上方的方格:即满足 (i le I) 以及 (J le j) 的 ((i,j))。
被一个或多个甜玉米洒水器喷洒到的方格可以长出甜玉米;被一个或多个苜蓿洒水器喷洒到的方格可以长出苜蓿。但是被两种洒水器均喷洒到(或均喷洒不到)的方格什么也长不出来。
帮助 Farmer John 求出在他的田地里安装洒水器的方案数((mod 10^9 + 7)),每个方格至多安装一个洒水器,使得每个方格均能生长作物(即被恰好一种洒水器喷洒到)。
某些方格正被长毛奶牛占据;这不会阻止这些方格生长作物,但是这些方格里不能安装洒水器。
(N leq 2000)
Solution
显然最后的状态会是中间有一条折线,左边是一种喷水器,右边是另一种,每个拐角处都必须安装,而其他地方随便安装(且种类确定)
设状态(dp[i][j])代表喷左下的喷水器的最右下的一个在((i,j))的方案数目(这个方案数目不包含右下角所填方案数,因为一会儿还要再往下转移),而(s[i][j])代表((i,j))右下方(包括i行和j列)一共有多少个可以填的地方
那么答案显然就是(sum dp[i][j] imes (s[i][j+1] - [j!=n]) imes [mapn[n][j+1]==1])
那考虑怎么转移,显然每个(dp[i][j])都可以从(dp[k][l](k<i,l<j))转移来
设上一个喷水器在((k,l))(即深红色块,而浅红色块是((i,j))),那么对于图示这一块灰色地区,是可以任意填的,
(图片来自洛谷题解,用户为水印所示)
所以得到一个很棒的转移:
(dp[i][j]=sum dp[k][l] imes 2^{s[k][l+1]-s[i][j+1]-1-[i>1]} imes [mapn[i-1][l+1]==1])
其中的细节一是特判边界是否要填另一种洒水器,二是判断假如要填的话能不能填上
但是这个转移还是(O(n^4))的呀 那不行
所以我们转化一下:
(dp[i][j]=sum (dp[k][l] imes {2^s[k][l+1]} imes [mapn[i-1][l+1]==1]) imes 2^{-s[i][j+1]-1-[i>1]})
那么显然前面那个东西可以二维前缀和一下:
(pre[i-1][l]=sum (dp[from 1 to i-1][l] imes 2^{s[from 1 to i-1][l+1]}) imes [mapn[i-1][l+1]==1])
(pre2=pre[i-1][from 1 to j-1])
然后就可以快乐(O(n^2))转移了,代码是很水的,就是边界特判有点烦
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=1e9+7;
int n;
int poww[4000010];
int ipow[4000010];
char ch[2010];
bool mapn[2010][2010];
int s[2010][2010];
int pre[2010];
int dp[2010][2010];
signed main(){
scanf("%lld",&n);
poww[0]=ipow[0]=1;
for(int i=1;i<=n*n;++i)poww[i]=2*poww[i-1]%mod,ipow[i]=(mod/2+1)*ipow[i-1]%mod;
for(int i=1;i<=n;++i){
scanf("%s",ch+1);
for(int j=1;j<=n;++j){
mapn[i][j]=(ch[j]=='.');
}
}
for(int i=1;i<=n;++i)mapn[0][i]=1;
for(int i=n;i>=0;--i){
for(int j=n;j>=0;--j){
s[i][j]=s[i][j+1]+s[i+1][j]-s[i+1][j+1]+mapn[i][j];
}
}
dp[0][0]=1;
pre[0]=poww[s[1][1]];
for(int i=1;i<=n;++i){
int pre2=0;
for(int j=1;j<=n;++j){
pre2=(pre2+(pre[j-1]*mapn[i-1][j])%mod)%mod;
if(!mapn[i][j])continue;
dp[i][j]=(pre2*ipow[s[i][j+1]+1+(i>1)])%mod;
}
for(int j=1;j<=n;++j)pre[j]=(pre[j]+(dp[i][j]*poww[s[i][j+1]])%mod);
}
int ans=0;
for(int i=0;i<n;++i){
ans=(ans+((pre[i]*mapn[n][i+1])%mod*ipow[1])%mod)%mod;
}
ans=(ans+pre[n])%mod;
printf("%lld
",ans);
}