题目描述
armer John 有一块小的田地,形状为一个 N 行 N 列的一个方阵(1≤N≤2000),对于所有的 1≤i,j≤N,从上往下的第 i 行的从左往右第 j 个方格记为 (i,j)。他有兴趣在他的田地里种植甜玉米和苜蓿。为此,他需要安装一些特殊的洒水器。
在方格 (I,J) 中的甜玉米洒水器可以喷洒到所有左下方的方格:即满足 I≤i 以及 j≤J 的 (i,j)。
在方格 (I,J) 中的苜蓿洒水器可以喷洒到所有右上方的方格:即满足 i≤I 以及 J≤j 的 (i,j)。
被一个或多个甜玉米洒水器喷洒到的方格可以长出甜玉米;被一个或多个苜蓿洒水器喷洒到的方格可以长出苜蓿。但是被两种洒水器均喷洒到(或均喷洒不到)的方格什么也长不出来。
帮助 FJ 求出在他的田地里安装洒水器的方法数(模 10^9+7),每个方格至多安装一个洒水器,使得每个方格均能生长作物(即被恰好一种洒水器喷洒到)。
某些方格正被长毛奶牛占据;这不会阻止这些方格生长作物,但是这些方格里不能安装洒水器。
N≤2000
题解
f[i][j]表示到ij的方案,每次向下或者向右折,加上转角的两个,前缀和优化
也可以直接维护到ij且上一步为哪个方向
code
#include <bits/stdc++.h>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define add(a,b) a=((a)+(b))%1000000007
#define mod 1000000007
#define two 500000004
#define ll long long
#define file
using namespace std;
ll f[2001][2001],p[2001],sum[2001],Sum,sum2,ans;
bool a[2001][2001];
int n,i,j,k,l;
char ch;
ll qpower(ll a,int b) {ll ans=1;while (b) {if (b&1) ans=ans*a%mod;a=a*a%mod;b>>=1;} return ans;}
int main()
{
freopen("sprinklers2.in","r",stdin);
#ifdef file
freopen("sprinklers2.out","w",stdout);
#endif
scanf("%d",&n);p[0]=1;
fo(i,1,n)
{
p[i]=p[i-1]*2%mod;
fo(j,1,n)
{
ch=getchar();while (ch!='.' && ch!='W') ch=getchar();
a[i][j]=ch=='W';sum[i]+=!a[i][j];
}
Sum+=sum[i];
}
// ---
fo(j,1,n)
{
if (j>1)
{
if (!a[1][j-1])
f[1][j]=p[sum[1]-1];
}
else
f[1][j]=p[sum[1]];
}
if (!a[1][n]) ans=qpower(2,Sum-1);
fo(i,1,n-1)
{
Sum-=sum[i];sum2=0;
fo(j,1,n)
{
add(f[i+1][j],f[i][j]*p[sum[i+1]]);
if (j>1 && !a[i+1][j-1]) add(f[i+1][j],sum2);
if (!a[i][j]) add(sum2,f[i][j]*p[sum[i+1]-1]%mod*two);
if (!a[i][j] && !a[i+1][n])
add(ans,f[i][j]*qpower(2,Sum-1)%mod*two);
}
}
fo(j,1,n)
if (!a[n][j])
add(ans,f[n][j]*two);
printf("%lld
",ans);
fclose(stdin);
fclose(stdout);
return 0;
}