【洛谷】P8614 [蓝桥杯 2014 省 A] 波动数列 的题解
题目传送门
思路
对于第
i
i
i 项,
i
>
1
i>1
i>1,我们都可以对第
i
−
1
i-1
i−1 项进行
+
a
+a
+a 或
−
b
-b
−b 操作得到第
i
i
i 项,最终
n
n
n 个数的和要等于
s
s
s。
我们不妨将对每一项的操作表示为
o
p
op
op,
o
p
op
op 有可以表示
+
a
+a
+a 操作也可以表示
−
b
-b
−b 操作,
o
p
[
i
]
op[i]
op[i] 表示第
i
+
1
i+1
i+1 项进行的操作。
设首项即第 1 1 1 项为 x x x,我们能够构造出的全部情况就是 s s = n × x + k × a − t × b ss = n \times x + k \times a - t \times b ss=n×x+k×a−t×b,其中, s s ss ss 表示我们构造的数列和。
k + t = 1 + 2 + … … + ( n − 1 ) = n × 2 k+t = 1 + 2 + …… + (n-1) = n \times 2 k+t=1+2+……+(n−1)=n×2
而我们要做的就是找到 s s = s ss = s ss=s 的方案数。
我们还可以发现对第 i i i 项进行操作时,它对整个数列之和的贡献为 o p × ( n − i + 1 ) op \times (n-i+1) op×(n−i+1),若 o p = − b op=-b op=−b,则贡献为 ( − b ) × ( n − i + 1 ) (-b) \times (n-i+1) (−b)×(n−i+1),若 o p = a op=a op=a,则贡献为 a × ( n − i + 1 ) a \times (n-i+1) a×(n−i+1)。
使用动态规划,dp[i][j]
表示前
i
i
i 个数中,和值为
j
j
j 的组合数。
递推关系,dp[i][j] = dp[i - 1][j] + dp[i - 1][j - i]
。
(由于规模较大,代码中我们使用滚动数组)
接下来我们的思路就变成了,枚举全部的 k k k,判断这种情况下是否满足上面的“整除关系”,若满足,我们就让答案加上枚举到的 k k k 所包含的方案数。
代码
#include <bits/stdc++.h>
#define endl "\n"
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
namespace fastIO {
inline int read() {
register int x = 0, f = 1;
register char c = getchar();
while (c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
inline void write(int x) {
if(x < 0) putchar('-'), x = -x;
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
return;
}
}
using namespace fastIO;
const ll MOD = 100000007;
ll dp[1000005] = {1LL};
ll ans, s, a, b;
int n;
int main() {
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
n = read();
scanf("%lld%lld%lld", &s, &a, &b);
for(int i = 1; i < n; i ++) {
for(int j = i * (i + 1) / 2; j >= i ;j --) {
dp[j] = (dp[j] + dp[j-i]) % MOD;
}
}
int m = n * (n - 1) / 2;
int c = a + b;
ll ss = s - m * b;
for(int i = 0; i <= m; i ++) {
if(ss % n == 0) {
ans = (ans + dp[i]) % MOD;
}
ss += c;
}
printf("%lld\n", ans);
return 0;
}