Codeforces 295D - Greg and Caves(dp)

题意:
给出一个 \(n \times m\) 的矩阵,需对其进行黑白染色,使得以下条件成立:

  • 存在区间 \([l,r]\)\(1\leq l\leq r\leq n\)),使得第 \(l,l+1,\dots,r\) 行恰有 \(2\) 个格子染成黑色,其余行所有格子均为白色。
  • 设第 \(i\) 行染黑的两个格子所在的列为 \(a_i,b_i(a_i\lt b_i)\),那么存在 \(l \leq t \leq r\),使得 \(a_l\geq a_{l+1}\geq a_{l+2}\geq\dots\geq a_t\leq a_{t+1}\leq a_{t+2}\leq\dots\leq a_r\)\(b_l\geq b_{l+1}\geq b_{l+2}\geq\dots\geq b_t\geq b_{t+1}\leq b_{t+2}\leq\dots\leq b_r\)
    \(n,m \in [1,2000]\)
    答案对 \(10^9+7\) 取模。

很显然可以分上部分和下部分考虑,这里考虑上半部分。
\(dp_{i,j}\) 表示填好了 \(i\) 行,第 \(i\) 行的“宽度”(\(b_i-a_i+1\))为 \(j\)
那么我们枚举上一行的宽度 \(k\),即 \(dp_{i,j}=\sum\limits_{k=2}^jdp_{i-1,k}\times(j-k+1)\)
前缀和优化可以搞到 \(n^2\)
然后我们考虑计算答案。
我们枚举上文中提到的 \(t\) 的位置,以及第 \(t\) 行的“宽度” \(w\),注意可能有多个 \(t\) 满足条件,这里我们枚举的是最下方的 \(t\),否则可能会重复计算。
这部分对答案的贡献为 \(f(t,w)=(\sum\limits_{i=1}^tdp_{i,w})\times(1+\sum\limits_{k=1}^{n-i}\sum\limits_{l=2}^{w-1}dp_{k,l}\times(w-l+1))\)
稍微解释一下这个式子。
前面的括号是填好上半部分的方案数,枚举行数求个和即可。
后面的括号的填好下半部分的方案数。第 \(t+1\) 行的宽度必然小于 \(w\),否则 \(t+1\) 也满足条件,我们枚举就不是“最下方的 \(t\)”了。
当然 \(t+1\) 行也可以不染色,贡献为 \(1\)
预处理三个前缀和就可以 \(\mathcal O(1)\) 求出 \(f(t,w)\)
最后 \(ans=\sum\limits_{t=1}^n\sum\limits_{w=2}^mf(t,w)\times(m-w+1)\)

#include <bits/stdc++.h>
using namespace std;
#define fi			first
#define se			second
#define pb			push_back
#define fz(i,a,b)	for(int i=a;i<=b;i++)
#define fd(i,a,b)	for(int i=a;i>=b;i--)
#define foreach(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
#define all(a)		a.begin(),a.end()
#define fill0(a)	memset(a,0,sizeof(a))
#define fill1(a)	memset(a,-1,sizeof(a))
#define fillbig(a)	memset(a,0x3f,sizeof(a))
#define y1			y1010101010101
#define y0			y0101010101010
typedef pair<int,int> pii;
typedef long long ll;
const ll MOD=1e9+7;
ll n,m,dp[2005][2005],s1[2005][2005],s2[2005][2005],s[2005][2005],_s[2005][2005],__s[2005][2005];
int main(){
	scanf("%d%d",&n,&m);
	for(int i=2;i<=m;i++) dp[1][i]=1;
	for(int j=2;j<=m;j++){
		s1[1][j]=(s1[1][j-1]+dp[1][j])%MOD;
		s2[1][j]=(s2[1][j-1]+dp[1][j]*j%MOD)%MOD;
	}
	for(int i=2;i<=n;i++){
		for(int j=2;j<=m;j++){
			dp[i][j]=(-s2[i-1][j]+s1[i-1][j]*(j+1)%MOD+MOD)%MOD;
//			printf("%d %d %lld\n",i,j,dp[i][j]);
		}
		for(int j=2;j<=m;j++){
			s1[i][j]=(s1[i][j-1]+dp[i][j])%MOD;
			s2[i][j]=(s2[i][j-1]+dp[i][j]*j%MOD)%MOD;
		}
	}
	ll ans=0;
	for(int i=1;i<=n;i++) for(int j=2;j<=m;j++) s[i][j]=(s[i-1][j]+dp[i][j])%MOD;
	for(int i=1;i<=n;i++) for(int j=2;j<=m;j++) _s[i][j]=(_s[i][j-1]+s[i][j])%MOD;
	for(int i=1;i<=n;i++) for(int j=2;j<=m;j++) __s[i][j]=(__s[i][j-1]+s[i][j]*j%MOD)%MOD;
	for(int i=1;i<=n;i++) for(int j=2;j<=m;j++){
		ans=(ans+s[i][j]*(_s[n-i][j-1]*(j+1)%MOD-__s[n-i][j-1]+MOD+1)%MOD*(m-j+1)%MOD)%MOD;
//		printf("%d %d %lld\n",i,j,s[i][j]*(_s[n-i][j-1]*(j+1)%MOD-__s[n-i][j-1]+MOD+1)%MOD*(m-j+1)%MOD);
	}
	printf("%lld\n",ans);
	return 0;
}
/*
dp[i][j]=\sum dp[i-1][k]*(j-k+1)
\sum s[x][k]*(j-k+1)
*/
posted @ 2020-10-12 20:01  tzc_wk  阅读(74)  评论(0编辑  收藏  举报