Codeforces 295D - Greg and Caves(dp)
题意:
给出一个 n×m 的矩阵,需对其进行黑白染色,使得以下条件成立:
- 存在区间 [l,r](1≤l≤r≤n),使得第 l,l+1,…,r 行恰有 2 个格子染成黑色,其余行所有格子均为白色。
- 设第 i 行染黑的两个格子所在的列为 ai,bi(ai<bi),那么存在 l≤t≤r,使得 al≥al+1≥al+2≥⋯≥at≤at+1≤at+2≤⋯≤ar,bl≥bl+1≥bl+2≥⋯≥bt≥bt+1≤bt+2≤⋯≤br。
n,m∈[1,2000]
答案对 109+7 取模。
很显然可以分上部分和下部分考虑,这里考虑上半部分。
设 dpi,j 表示填好了 i 行,第 i 行的“宽度”(bi−ai+1)为 j。
那么我们枚举上一行的宽度 k,即 dpi,j=j∑k=2dpi−1,k×(j−k+1)。
前缀和优化可以搞到 n2。
然后我们考虑计算答案。
我们枚举上文中提到的 t 的位置,以及第 t 行的“宽度” w,注意可能有多个 t 满足条件,这里我们枚举的是最下方的 t,否则可能会重复计算。
这部分对答案的贡献为 f(t,w)=(t∑i=1dpi,w)×(1+n−i∑k=1w−1∑l=2dpk,l×(w−l+1))
稍微解释一下这个式子。
前面的括号是填好上半部分的方案数,枚举行数求个和即可。
后面的括号的填好下半部分的方案数。第 t+1 行的宽度必然小于 w,否则 t+1 也满足条件,我们枚举就不是“最下方的 t”了。
当然 t+1 行也可以不染色,贡献为 1。
预处理三个前缀和就可以 O(1) 求出 f(t,w)。
最后 ans=n∑t=1m∑w=2f(t,w)×(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) */
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步