题目:
输入输出:
题目分析:
首先假设第一个数为 x,那么后面的数都是在 x的基础上进行加 a或者减 b操作,将这一操作用 P={a,-b}来表示,那么最终的和一定为:
x
+
(
x
+
P
)
+
(
x
+
2
P
)
+
.
.
.
+
(
x
+
(
n
−
1
)
P
)
=
s
x + (x+P) + (x+2P) + ... +(x+(n-1)P) = s
x+(x+P)+(x+2P)+...+(x+(n−1)P)=s
变换一下上述的等式为:
n
x
+
(
1
+
2
+
3
+
.
.
.
+
n
−
1
)
P
=
s
nx + (1+2+3+...+n-1)P = s
nx+(1+2+3+...+n−1)P=s
通过上述公式可知 P的总个数是一定的,为:n(n-1)/2 ,并且由于 x为整数,那么
x
=
(
s
−
n
(
n
−
1
)
/
2
∗
P
)
/
n
x = (s - n(n-1)/2*P) / n
x=(s−n(n−1)/2∗P)/n
依据上述的说明,我们可以就其中的 a的个数进行讨论,a的个数和的范围是 [0,n(n-1)/2],可以使用动态规划来进行分析:
dp[i][j]用于表示前 i个元素组成和为 j的方案数,那么有如下的情况:
- 当 i 大于 j 时,由于 i 的系数为 i ,所以 dp[i][j]必然是从dp[i-1][j]转换来的,也就是说不能使用 i
- 当 i 小于等于 j 时,由于j大于 i,而i又可以 使用 或者 不使用,所以有下面的转换:
dp[i][j] = dp[i-1][j] + dp[i-1][j-i]
按照上述的规则,i的范围应该为 1到 n-1,j为0到n(n-1)/2,显然内存会占用过多,再观察上述公式,发现当前的状态只是与前一个状态有关,所以可以使用滚动数组进行解决。
代码展示:
#include <iostream>
#include <cstring>
#include <cstdio>
#define MOD 100000007
using namespace std;
const int MAXN = 1005;
long long dp[2][MAXN*MAXN];
long long n,s,a,b;
int main()
{
scanf("%lld%lld%lld%lld",&n,&s,&a,&b);
dp[0][0] = 1;
int flag = 0;
for(int i=1;i<n;++i)
{
flag = 1 - flag;
for(int j=0;j<=i*(i+1)/2;++j)
if(j < i)
dp[flag][j] = dp[1-flag][j];
else
dp[flag][j] = (dp[1-flag][j]%MOD + dp[1-flag][j-i]%MOD)%MOD;
}
long long ans,cnt = 0;
for(int i=0;i<=n*(n-1)/2;++i)
{
ans = s - i*a + (n*(n-1)/2-i)*b;
if(ans % n == 0)
cnt = (cnt%MOD+dp[flag][i]%MOD)%MOD;
}
printf("%lld\n",cnt);
return 0;
}