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

Posted youddjxd

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[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 求出在他的田地里安装洒水器的方案数((mod 10^9 + 7)),每个方格至多安装一个洒水器,使得每个方格均能生长作物(即被恰好一种洒水器喷洒到)。

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

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])

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

然后转化一下:

(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[1~i-1][l] imes 2^{s[1~i-1][l+1]}) imes [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
",ans);
}

以上是关于[USACO 2020 Open Platinum]Sprinklers 2: Return of the Alfalfa的主要内容,如果未能解决你的问题,请参考以下文章