题目
思路来源
qls
题解
一道很久之前的题,看当时有人问,但自己没有补,现在才彻底想明白
将所求表达式考虑成这样一个匹配问题,
dp[i][j][k]表示当前考虑前i个数,还有j个左边的值要连向比它们大的右边的值(同时意味着还有j个右边的值要连向比它们大的左边的值),当前的绝对值之和是k的方案数
转移即加入第i种数,
此时分五种情况,以同时加入2左、2右为例,
注意到比和自己小的数匹配有j种方式,
①左右等,2左和2右匹配,dp[i][j][k+2*j]+=dp[i][j][k]
②左配右留,2左和右边已经出现的数匹配,2右留到以后再说,dp[i][j][k+2*j]+=dp[i][j][k]*j
③左留右配,2右和左边已经出现的数匹配,2左留到以后再说,dp[i][j][k+2*j]+=dp[i][j][k]*j
④左留右留,2左和2右都留到以后再说,dp[i][j+1][k+2*(j+1)]+=dp[i][j][k]
⑤左配右配,2左和右边已经出现的数匹配,2右和左边已经出现的数匹配,dp[i][j-1][k+2*(j-1)]+=dp[i][j][k]*j*j
注意到前三种可以合并,于是就分三种转移
tutorial死活看不懂,自己知道dp式的含义之后推一推就推出来了
代码
#include<bits/stdc++.h>
using namespace std;
const int N=51,mod=1e9+7;
int n,k,dp[N][N][N*N];
void add(int &x,int y){
x=(x+y)%mod;
}
int main(){
scanf("%d%d",&n,&k);
dp[0][0][0]=1;
//dp[1][1][0]
for(int i=0;i<n;++i){
for(int j=0;j<=i;++j){
for(int l=0;l<=k;++l){
if(!dp[i][j][l])continue;
if(l+2*j<=k)add(dp[i+1][j][l+2*j],1ll*(2*j+1)*dp[i][j][l]%mod);
if(l+2*(j+1)<=k)add(dp[i+1][j+1][l+2*(j+1)],dp[i][j][l]);
if(j-1>=0 && l+2*(j-1)<=k)add(dp[i+1][j-1][l+2*(j-1)],1ll*(j*j)*dp[i][j][l]%mod);
}
}
}
printf("%d\n",dp[n][0][k]);
return 0;
}