题目内容
观察这个数列:
1 3 0 2 -1 1 -2 …
这个数列中后一项总是比前一项增加2或者减少3,且每一项都为整数。
栋栋对这种数列很好奇,他想知道长度为 n 和为 s ,而且后一项总是比前一项增加 a 或者减少 b 的整数数列可能有多少种呢?
输入格式
共一行,包含四个整数n,s,a,b
,含义如前面所述。
输出格式
共一行,包含一个整数,表示满足条件的方案数。
由于这个数很大,请输出方案数除以 100000007的余数。
数据范围
1 ≤ n ≤ 1000 , − 1 0 9 ≤ s ≤ 1 0 9 , 1 ≤ a , b ≤ 1 0 6 1≤n≤1000 , −10^9≤s≤10^9 , 1≤a,b≤10^6 1≤n≤1000,−109≤s≤109,1≤a,b≤106
样例
输入样例:
4 10 2 3
输出样例:
2
样例解释
两个满足条件的数列分别是2 4 1 3和7 4 1 -2。
题目以及算法复杂度分析
假设我们的数列是从x
开始的,也就是说x
是数列的第一项。那么我们第一时间想到的是去枚举后一项所有的可能,即每一项的下一项有两种可能:加上a或者是减去b,如此我们枚举每一项,然后记录当前数列全部项的和。根据这个思路,我们需要定义一个二维数组,第一维是数列的项数,第二维是数列当前的和。如此一来,第二维就需要足足
1
0
9
10^9
109个长度,在枚举的时候显然是TLE的。
接下来思考更优的方法,我们首先把这个数列表示出来
x
+
(
x
+
d
1
)
+
(
x
+
d
1
+
d
2
)
+
⋯
+
(
x
+
d
1
+
d
2
+
d
3
+
⋯
+
d
n
−
1
)
=
s
x + (x + d_1) + (x + d_1 + d_2) + \dots + (x + d_ 1 + d_ 2 + d_3 + \dots + d_{n-1} ) = s
x+(x+d1)+(x+d1+d2)+⋯+(x+d1+d2+d3+⋯+dn−1)=s
其中
d
i
d_i
di的取值为
a
a
a或
−
b
-b
−b,这样一来,其实我们不能够确定范围的只有
x
x
x,因此我们就利用最朴素的数学思维,用已知表示未知
n
x
+
(
n
−
1
)
d
1
+
(
n
−
2
)
d
2
+
⋯
+
2
d
n
−
2
+
d
n
−
1
=
s
nx + (n - 1)d_1 + (n - 2)d_2 + \dots + 2d_{n - 2} + d_{n-1} = s
nx+(n−1)d1+(n−2)d2+⋯+2dn−2+dn−1=s
进而有
x
=
s
−
(
n
−
1
)
d
1
+
(
n
−
2
)
d
2
+
⋯
+
2
d
n
−
2
+
d
n
−
1
n
x = \frac{s - (n - 1)d_1 + (n - 2)d_2 + \dots + 2d_{n - 2} + d_{n-1} }{n}
x=ns−(n−1)d1+(n−2)d2+⋯+2dn−2+dn−1
到此我们可以很容易的看出,
x
x
x最明显的特征就是应该是一个整数(题目中强调了需要保证数列的所有项都是整数),因此这就是我们的判断
x
x
x是否合法的最重要标准。到此为止,我们所求的数列的个数,就转化成了求合法
x
x
x的个数,是一种不重不漏的求法。
x
x
x合法即当
x
x
x是整数的时候,即分子的每一项都是与
s
s
s模
n
n
n同余的,因此我们只需要记录分子上除去
s
s
s每一项对于
n
n
n的余数,最取出与
n
n
n同余的数量即可。这样就可以定义一个dp数组f[i][j]
第一维表示当前数列的长度,第二维表示前
(
n
−
i
)
d
i
(n - i)d_i
(n−i)di之和模
n
n
n的余数。算法复杂度也非常简单,由于这里的dp数组有两个维度,并且都是
O
(
n
)
O(n)
O(n)的级别,因此算法复杂度为
O
(
n
2
)
O(n^2)
O(n2),其中
n
<
1000
n < 1000
n<1000
完整代码
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 1010, mod = 100000007;
int f[N][N];
int get_mod(int a, int b) //这里的作用是取正数模,和数学中的余数操作一致
{
return (a % b + b) % b;
}
int main()
{
int n, s, a, b;
cin >> n >> s >> a >> b;
f[0][0] = 1; //初始化,长度为0的数列只有一个
for(int i = 1; i < n; i ++)
for(int j = 0; j < n; j ++)
f[i][j] = (f[i - 1][get_mod(j - (n - i) * a, n)] + f[i - 1][get_mod(j + (n - i) * b, n)]) % mod;
cout << f[n - 1][get_mod(s, n)] << endl;
return 0;
}