题目说实话看起来吓人,实则不难
不过仔细阅读两位前辈的题解后
发现两位前辈对循环边界部分处理不算太清楚
这里写一篇补全一下
首先状态设计:dp[i][j][k][sum]
代表当前是第i个数字,且第i位选择数字j,
选择数字j后形成的递减序列长度为k
前i个数字的和为sum
此时的方案数为dp[i][j][k][sum]
显然分析,k只有1和2两种情况
1的时候就是当前选的数字比上一个大(所以说是当前递减数列的第一个数字)
2的时候就是当前选的数字比上一个小(不可能出现比上两个小的情况,因为这样序列长度就是3不合题了)
那么状态转移方程也能写了
我们需要枚举一下上一个数字的值
依靠这个进行转移
先不管这个数字是否可以自定(如果要自定就再加一层循环枚举这个数字)
如果上一个的值大于当前值
dp[i][当前值][2][之前的和+当前值]+=dp[i-1][上一位值][1][之前的和]
因为这个时候形成递减数列了,所以递减数列的长度是2,就只转移到长度为2的状态
如果当前值大于等于上一个值
dp[i][当前值][1][之前的和+当前值]+=dp[i-1][之前值][1][之前和]+dp[i-1][之前值][2][之前和]
讨论一下枚举的界限问题
先放代码:
{
if(a[i]==-1)
{
for(int j=0;j<=40;j++)//枚举当前数
{
for(int k=0;k<=40;k++)//上一位数
{
for(int s=j*(i-1);s<=40*(i-1);s++)
{
if(j>=k)
{
dp[i][j][1][s+j]+=dp[i-1][k][1][s]+dp[i-1][k][2][s];
dp[i][j][1][s+j]%=mod;
}
else
{
dp[i][j][2][s+j]+=dp[i-1][k][1][s];
dp[i][j][2][s+j]%=mod;
}
}
}
}
}
else
{
for(int k=0;k<=40;k++)
{
for(int s=a[i]*(i-1);s<=40*(i-1);s++)
{
if(a[i]>=k)
{
dp[i][a[i]][1][s+a[i]]+=dp[i-1][k][1][s]+dp[i-1][k][2][s];
dp[i][a[i]][1][s+a[i]]%=mod;
}
else
{
dp[i][a[i]][2][s+a[i]]+=dp[i-1][k][1][s];
dp[i][a[i]][2][s+a[i]]%=mod;
}
}
}
}
}
为了方便只讨论自定部分(不自定的显然更容易理解)
当前数和上一位的数(j和k显然可以随意选)
但是之前和这里几位大佬的上下界设置都很随意
下界设置成j*(i-1)因为当前的数字一定要小于等于之前的平均数
所以之前的数字和就设置为j*(i-1)
一共i-1个数字,每个最大是40
所以上限设为40*(i-1)
代码是从第2位开始考虑的
显然要做i=1的时候的初始化
如果第一个数字可以自定值
那么dp[1][0--40][1][0--40]都是1
如果不可以 dp[1][a[1]][1][a[1]]为1其他为0
最后找答案的时候也是
先枚举序列最后一个数字
然后从这个数乘n到1600
每一个都加入答案即可
int ans=0;
for(int i=0;i<=40;i++)
{
for(int j=i*n;j<=1600;j++)
ans+=dp[n][i][1][j]+dp[n][i][2][j],ans%=mod;
}
完事收工