题目说实话看起来吓人,实则不难

不过仔细阅读两位前辈的题解后

发现两位前辈对循环边界部分处理不算太清楚

这里写一篇补全一下

首先状态设计: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;
	}

完事收工