首先考虑一个错误做法:令dp[i][j]表示前i个操作有j个黑球的方案数,这样显然会重复计数。
改为dp[i][j][k]表示前i个操作,用过箱子里前j+k个球,其中j个是黑球,k个是白球的方案数,然后贪心转移,这样显然正确性是对的,但是复杂度原地爆炸。
考虑第一个做法,显然一个输出序列对应初始黑球数量是一段区间,而之所以不能比下界还要低是因为若如此就会途中发生没有黑球的情况,因此我们强制要求操作期间有一个时刻黑球数目变成0,再开一维记录这个即可。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define N 3010
#define mod 1000000007
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define upd(x,y) (x+=y,(x>=mod?x-=mod:0))
using namespace std;
int dp[2][N][2];
int main()
{
int n,m,ans=0,now=0,nxt=1;
scanf("%d%d",&n,&m);
rep(i,0,n) dp[now][i][i==0]=1;
for(int i=0;i<m;i++,swap(now,nxt)) rep(j,0,n)
{
if(j>0)
upd(dp[nxt][j][j==1],dp[now][j][0]),
upd(dp[nxt][j-1][j==1],dp[now][j][0]),
upd(dp[nxt][j][1],dp[now][j][1]),
upd(dp[nxt][j-1][1],dp[now][j][1]);
if(j<n)
upd(dp[nxt][j][0],dp[now][j][0]),
upd(dp[nxt][j+1][0],dp[now][j][0]),
upd(dp[nxt][j][1],dp[now][j][1]),
upd(dp[nxt][j+1][1],dp[now][j][1]);
dp[now][j][0]=dp[now][j][1]=0;
}
for(int i=0;i<=n;i++) upd(ans,dp[now][i][1]);
return !printf("%d\n",ans);
}