题目大意:
在一个mxn网格上,填充格子为黑色构成山形,定义每一列最高的黑色格子的高度为该列的高度h,需要满足以下要求:
有一对l,r,满足1<=l<=r<=n
在1<=i<=l范围内,
h
i
h_i
hi不下降
l<=i<=r范围内,
h
i
h_i
hi保持不变
在r<=i<=n内,
h
i
h_i
hi不上升
求填充格子的方案数。
思路
好似可以dp,但我理不清转移Orz。考虑从整体进行组合计算。
考虑枚举l,r。若我们只是把l和r当作题目中的l和r,那么,不下降和不上升的不变部分可能会导致重复,所以,我们需要考虑枚举的l和r为平稳部分的边界。
则,枚举的上升部分列数为l-1,下降部分列数为n-r。接下来还是得考虑枚举平稳部分的高度h,则,上升部分和下降部分分别上升和下降h,考虑划分h。
我们考虑一个经典问题:方程
x
1
+
x
2
+
x
3
+
…
…
+
x
n
=
h
x_1+x_2+x_3+……+x_n=h
x1+x2+x3+……+xn=h的解的个数,但是,因为l和r是边界条件,所以第一个元
x
1
x_1
x1需要大于0保证l和r是边界。因为,相当于方程
(
x
1
+
1
)
+
x
2
+
x
3
+
…
…
+
x
n
=
h
(x_1+1)+x_2+x_3+……+x_n=h
(x1+1)+x2+x3+……+xn=h这个方程的解的个数,也就是
x
1
+
x
2
+
x
3
+
…
…
+
x
n
=
h
−
1
x_1+x_2+x_3+……+x_n=h-1
x1+x2+x3+……+xn=h−1的解的个数。答案为
C
h
+
n
−
1
n
−
1
C_{h+n-1}^{n-1}
Ch+n−1n−1,最后还要+1(全白的情况)
据此,我们可以知道答案为
∑
h
=
1
m
∑
l
=
1
n
∑
r
=
l
n
C
l
+
h
−
2
k
−
1
∗
C
h
−
r
+
k
−
1
k
−
1
\sum_{h=1}^{m}\sum_{l=1}^{n}\sum_{r=l}^nC_{l+h-2}^{k-1}*C_{h-r+k-1}^{k-1}
h=1∑ml=1∑nr=l∑nCl+h−2k−1∗Ch−r+k−1k−1
枚举三个变量肯定不行,考虑变形:
∑
h
=
1
m
∑
l
=
1
n
∑
r
=
l
n
C
l
+
h
−
2
k
−
1
∗
C
h
−
r
+
k
−
1
k
−
1
=
∑
h
=
1
m
∑
l
=
1
n
C
l
+
h
−
2
k
−
1
∑
r
=
l
n
C
h
−
r
+
k
−
1
k
−
1
\sum_{h=1}^{m}\sum_{l=1}^{n}\sum_{r=l}^nC_{l+h-2}^{k-1}*C_{h-r+k-1}^{k-1} \\ =\sum_{h=1}^{m}\sum_{l=1}^{n}C_{l+h-2}^{k-1}\sum_{r=l}^nC_{h-r+k-1}^{k-1}\\
h=1∑ml=1∑nr=l∑nCl+h−2k−1∗Ch−r+k−1k−1=h=1∑ml=1∑nCl+h−2k−1r=l∑nCh−r+k−1k−1
考虑将最后一项递推预处理掉就行了(虽然这个预处理我想了好一会)
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define IOS ios_base::sync_with_stdio(0); cin.tie(0);
#define rep(i, a, n) for(int i = a; i <= n; ++ i)
#define per(i, a, n) for(int i = n; i >= a; -- i)
//#define ONLINE_JUDGE
using namespace std;
typedef long long ll;
const int mod=1e9+7;
template<typename T>void write(T x)
{
if(x<0)
{
putchar('-');
x=-x;
}
if(x>9)
{
write(x/10);
}
putchar(x%10+'0');
}
template<typename T> void read(T &x)
{
x = 0;char ch = getchar();ll f = 1;
while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
int gcd(int a,int b){return b==0?a:gcd(b,a%b);}
int lcm(int a,int b){return a/gcd(a,b)*b;};
ll ksm(ll a,ll n){
ll ans=1;
while(n){
if(n&1) ans=(ans*a)%mod;
a=a*a%mod;
n>>=1;
}
return ans%mod;
}
//==============================================================
const int maxn=4e3+10;
int n,m;
ll f[maxn],finv[maxn];
int sum[maxn];
void init(){
f[0]=1;
for(int i=1;i<maxn;++i)f[i]=f[i-1]*i%mod;
finv[maxn-1]=ksm(f[maxn-1],mod-2);
for(int i=maxn-2;i>=0;--i){
finv[i]=finv[i+1]*(i+1)%mod;
}
}
ll C(ll n,ll m){
//if(n<m)return 0;
return f[n]*finv[n-m]%mod*finv[m]%mod;
}
ll pre[maxn][maxn];
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
//clock_t c1 = clock();
//===========================================================
read(n),read(m);
init();
for(int l=n;l>=1;--l){
for(int j=m;j>=0;--j){
pre[l][j]=(pre[l+1][j]+C(n-l+j-1,j-1))%mod;
}
}
ll ans=0;
for(int k=1;k<=m;++k){
for(int l=1;l<=n;++l){
ans+=C(k-1+l-1,k-1)*pre[l][k]%mod;
ans%=mod;
}
}
cout<<(ans+1)%mod<<endl;
//===========================================================
//std::cerr << "Time:" << clock() - c1 << "ms" << std::endl;
return 0;
}