CF295D Solution

题解

设dp状态(dp[i][j])表示前(i)行黑色单元格间列数(含黑色)单调不降且第(i)行列数为(j)的方案数,也就是(t)行及以上部分的方案数。因为洞的左右位置不影响它的形态,所以只需记录列数而非左右端点。(dp[i][j]=sumlimits_{k=2}^mdp[i-1][k] imes (j-k+1)),可以发现等式右侧是(dp[i-1])前缀和的前缀和,可以(O(1))求出。(t)行以下的部分与上部相同,也可使用(dp)数组。枚举(t),为避免重复计算,令行([i,t])中没有黑色单元格间列数与第(t)行相等的行,答案增加((dp[t][j]-dp[t-1][j]) imes dp[n-t+1][j])(上部方案数乘下部方案数)。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=2010,mod=1e9+7;
int dp[N][N];
inline int read()
{
    int s=0,w=1; char ch=getchar();
    while(ch<'0' || ch>'9') {if(ch=='-') w=-1; ch=getchar();}
    while(ch>='0' && ch<='9') s=s*10+ch-'0',ch=getchar();
    return s*w;
}
int main()
{
    int n=read(),m=read(),sum1,sum2,ans=0;
    for(int i=2;i<=m;i++) dp[1][i]=1;
    for(int i=2;i<=n;i++)
    {
        sum1=sum2=0;
        for(int j=2;j<=m;j++)
        {
            sum1=(sum1+dp[i-1][j])%mod,sum2=(sum2+sum1)%mod;
            dp[i][j]=(sum2+1)%mod;
        }
    }
    for(int i=2;i<=m;i++)
    {
        for(int j=1;j<=n;j++)
            ans=(ans+1ll*(dp[j][i]-dp[j-1][i]+mod)*dp[n-j+1][i]%mod*(m-i+1)%mod)%mod;
    }
    printf("%d",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/violetholmes/p/15394439.html