zoukankan      html  css  js  c++  java
  • [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)),每个方格至多安装一个洒水器,使得每个方格均能生长作物(即被恰好一种洒水器喷洒到)。

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

    (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);
    }
    
  • 相关阅读:
    关于C的struct结构的几个常见疑问。
    NAT类型
    Linux oops stack 分析
    关于pci_dev的定义
    还是动态添加控件 触发事件
    页面编译模型
    MDX 销售额与上月比较
    C语言的词法语法分析lex&yacc 经典收藏
    interrupt storm
    js获取mac地址
  • 原文地址:https://www.cnblogs.com/youddjxd/p/12899620.html
Copyright © 2011-2022 走看看