[USACO 2020 Open Platinum]Sprinklers 2: Return of the Alfalfa

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 求出在他的田地里安装洒水器的方案数(\(\bmod \ 10^9 + 7\)),每个方格至多安装一个洒水器,使得每个方格均能生长作物(即被恰好一种洒水器喷洒到)。

某些方格正被长毛奶牛占据;这不会阻止这些方格生长作物,但是这些方格里不能安装洒水器。

Solution

显然最后的状态会是中间有一条折线,左边是一种喷水器,右边是另一种,每个拐角处都必须安装,而其他地方随便安装(且种类确定)

设状态\(dp[i][j]\)代表喷左下的喷水器的最右下的一个在\((i,j)\)的方案数目(这个方案数目不包含右下角所填方案数,因为一会儿还要再往下转移),而\(s[i][j]\)代表\((i,j)\)右下方(包括i行和j列)一共有多少个可以填的地方

那么答案显然就是\(\sum dp[i][j] \times (s[i][j+1] - [j!=n]) \times [mapn[n][j+1]==1]\)

那考虑怎么转移,显然每个\(dp[i][j]\)都可以从\(dp[k][l](k<i,l<j)\)转移来

设上一个喷水器在\((k,l)\)(即深红色块,而浅红色块是\((i,j)\)),那么对于图示这一块灰色地区,是可以任意填的,

扫描二维码关注公众号,回复: 11205359 查看本文章

(图片来自洛谷题解,用户为水印所示)

所以得到一个很棒的转移:

\(dp[i][j]=\sum dp[k][l] \times 2^{s[k][l+1]-s[i][j+1]-1-[i>1]} \times [mapn[i-1][l+1]==1]\)

其中的细节一是特判边界是否要填另一种洒水器,二是判断假如要填的话能不能填上

然后转化一下:

\(dp[i][j]=\sum (dp[k][l] \times {2^s[k][l+1]} \times [mapn[i-1][l+1]==1]) \times 2^{-s[i][j+1]-1-[i>1]}\)

那么显然前面那个东西可以二维前缀和一下:

\(pre[i-1][l]=\sum (dp[1~i-1][l] \times 2^{s[1~i-1][l+1]}) \times [mapn[i-1][l+1]==1]\)

\(pre2=pre[i-1][1~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\n",ans);
}

猜你喜欢

转载自www.cnblogs.com/youddjxd/p/12899620.html