我们可以把积木看成数字
使用 DP,定义 dp[i][j] 为放了长度为 i,使用数数量为 j
我们把它倒过来加,第 j 个数可以放在序列的左边或者右边
很容易得到两个状态转移方程:
dp[i][j]=dp[i−1][j] 如果上一个数是放在整个序列的左边,而这一个数也是放在左边。
我们发现,如果在这两个数之间,还有别的数,每两个数可以组成相上面的式子的一种情况。
那么我们得到一个式子:
dp[i][j]=dp[i-1][j-i+1] 如果上一个数是放在整个序列的左边,而这一个数是放在右边,我们需要先把这个数从左边运到右边,这个“运”的过程要执行 $i$ 次。
我们发现,如果在这两个数之间,还有别的数,每两个数可以组成相上面的式子的一种情况。
那么我们得到一个式子:
我们得到一个n^3 的做法
for(int i=3;i<=n;i++){
for(int j=n-2;j>0;j--){
for(int k=0;j<=n;j+=2){
dp[i][j]+=dp[i-1][j+1+k];
if(j+k+1>i) dp[i][j]+=dp[i-1][j-i+1+k];
}
}
}
但是 n^3 显然过不了这道题
我们加一个 sum 数组,前缀和优化一下就行了
#include<bits/stdc++.h>
#define mod 1000000007
using namespace std;
int n,k,dp[5005][5005],sum[5005];
int plas(int x,int y){
if(x+y>=mod) return x+y-mod;
else return x+y;
}
int times(int x,int y){
return (long long) x*y%mod;
}
int main(){
cin>>n>>k;
dp[2][n-1]=2;
for(int i=3;i<=n;i++){
sum[n-1]=dp[i-1][n-1];
for(int j=n-2;j>0;j--)
sum[j]=plas(sum[j+2],dp[i-1][j]);
for(int j=n-2;j>0;j--){
dp[i][j]=sum[j+1];
if(j+i<=n) dp[i][j]=plas(dp[i][j],sum[j+i-1]);
}
}
int ans=0;
for(int i=2;i<=min(n,k);i++){
for(int j=1;j<n;j++){
ans+=times(dp[i][j],(k-i+1));
ans%=mod;
}
}
cout<<ans;
return 0;
}