6544. 【USACO 2020 US Open Platinum】 Sprinklers 2: Return of the Alfalfa

题目描述

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\n",ans);
	
	fclose(stdin);
	fclose(stdout);
	
	return 0;
}
posted @ 2020-04-12 11:43  gmh77  阅读(324)  评论(0编辑  收藏  举报