Processing math: 100%

Codeforces 295D - Greg and Caves(dp)

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

  • 存在区间 [l,r]1lrn),使得第 l,l+1,,r 行恰有 2 个格子染成黑色,其余行所有格子均为白色。
  • 设第 i 行染黑的两个格子所在的列为 ai,bi(ai<bi),那么存在 ltr,使得 alal+1al+2atat+1at+2arblbl+1bl+2btbt+1bt+2br
    n,m[1,2000]
    答案对 109+7 取模。

很显然可以分上部分和下部分考虑,这里考虑上半部分。
dpi,j 表示填好了 i 行,第 i 行的“宽度”(biai+1)为 j
那么我们枚举上一行的宽度 k,即 dpi,j=jk=2dpi1,k×(jk+1)
前缀和优化可以搞到 n2
然后我们考虑计算答案。
我们枚举上文中提到的 t 的位置,以及第 t 行的“宽度” w,注意可能有多个 t 满足条件,这里我们枚举的是最下方的 t,否则可能会重复计算。
这部分对答案的贡献为 f(t,w)=(ti=1dpi,w)×(1+nik=1w1l=2dpk,l×(wl+1))
稍微解释一下这个式子。
前面的括号是填好上半部分的方案数,枚举行数求个和即可。
后面的括号的填好下半部分的方案数。第 t+1 行的宽度必然小于 w,否则 t+1 也满足条件,我们枚举就不是“最下方的 t”了。
当然 t+1 行也可以不染色,贡献为 1
预处理三个前缀和就可以 O(1) 求出 f(t,w)
最后 ans=nt=1mw=2f(t,w)×(mw+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 @   tzc_wk  阅读(83)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示