1214. 波动数列
观察这个数列:
1 3 0 2 -1 1 -2 …
这个数列中后一项总是比前一项增加2或者减少3,且每一项都为整数。
栋栋对这种数列很好奇,他想知道长度为 n 和为 s 而且后一项总是比前一项增加 a 或者减少 b 的整数数列可能有多少种呢?
输入格式
共一行,包含四个整数 n,s,a,b,含义如前面所述。
输出格式
共一行,包含一个整数,表示满足条件的方案数。
由于这个数很大,请输出方案数除以 100000007 的余数。
数据范围
1≤n≤1000,
−109≤s≤109,
1≤a,b≤106
输入样例:
4 10 2 3
输出样例:
2
样例解释
两个满足条件的数列分别是2 4 1 3和7 4 1 -2。
题解:
此题应该是一个组合问题
设这个数列第一项为x,设 d ∈{ +a, -b } ,则长度为n的序列所有的项为
x
,
x
+
d
1
,
x
+
d
2
,
x
+
d
3
,
.
.
.
,
x
+
d
n
−
1
x , x + d_1, x + d_2, x + d_3, ... , x + d_{n-1}
x,x+d1,x+d2,x+d3,...,x+dn−1
他的和为
n
x
+
(
n
−
1
)
d
1
+
(
n
−
2
)
d
2
+
(
n
−
3
)
d
3
+
.
.
.
+
d
n
−
1
=
s
nx + (n-1)d_1 + (n-2)d_2 + (n-3)d_3 + ... + d_n-1 = s
nx+(n−1)d1+(n−2)d2+(n−3)d3+...+dn−1=s
因为x的大小我们无法确定,所以我们把公式变形如下
x
=
(
s
−
(
(
n
−
1
)
d
1
+
(
n
−
2
)
d
2
+
(
n
−
3
)
d
3
+
.
.
.
+
d
n
−
1
)
)
n
x =\frac{( s-( (n-1)d_1+ (n-2)d_2 + (n-3)d_3 + ... + d_{n-1}) )}{n}
x=n(s−((n−1)d1+(n−2)d2+(n−3)d3+...+dn−1))
求满足这个式子 的方案数有多少,因为x一定是整数,所以
(
s
−
(
(
n
−
1
)
d
1
+
(
n
−
2
)
d
2
+
(
n
−
3
)
d
3
+
.
.
.
+
d
n
−
1
)
)
( s-( (n-1)d_1+ (n-2)d_2 + (n-3)d_3 + ... + d_{n-1}) )
(s−((n−1)d1+(n−2)d2+(n−3)d3+...+dn−1)) % n一定为 0 ,因此推出s % n ==
(
n
−
1
)
d
1
+
(
n
−
2
)
d
2
+
(
n
−
3
)
d
3
+
.
.
.
+
d
n
−
1
(n-1)d_1+ (n-2)d_2 + (n-3)d_3 + ... + d_{n-1}
(n−1)d1+(n−2)d2+(n−3)d3+...+dn−1的和 % n,也就是两者的模n的余数必须相同
s是确定的,
所以我们最后就是要求
(
n
−
1
)
d
1
+
(
n
−
2
)
d
2
+
(
n
−
3
)
d
3
+
.
.
.
+
d
n
−
1
(n-1)d_1+ (n-2)d_2 + (n-3)d_3 + ... + d_{n-1}
(n−1)d1+(n−2)d2+(n−3)d3+...+dn−1这个式子的所有可能的和模n的余数是s%n的结果数
——————————————————分割线————————————————————————————
是一个dp组合问题:
状态表示:设
f
[
i
]
[
j
]
f[i][j]
f[i][j]为选了i个数,前 i 个 d 的和模 n 的余数为 j 的集合的数量
明确目标:最后求得是
f
[
n
−
1
]
[
s
%
n
]
f[n-1][s\%n]
f[n−1][s%n]的值
递推关系:
第 i 次选择时可以选 +a 也可以选 -b
如果选 + a,前i个数的和为
[
(
n
−
1
)
d
1
+
(
n
−
2
)
d
2
+
.
.
.
+
(
n
−
i
−
1
)
d
i
+
1
+
(
n
−
i
)
a
]
%
n
≡
j
%
n
[(n-1)d_1~ + (n-2)d_2~ +... +(n-i-1)d_{i+1}+(n-i)a]\%n≡j\%n
[(n−1)d1 +(n−2)d2 +...+(n−i−1)di+1+(n−i)a]%n≡j%n
(
n
−
1
)
d
1
+
(
n
−
2
)
d
2
+
.
.
.
+
(
n
−
i
−
1
)
d
i
+
1
≡
j
−
(
n
−
i
)
a
(n-1)d_1~ + (n-2)d_2~ +... +(n-i-1)d_{i+1}≡j-(n-i)a
(n−1)d1 +(n−2)d2 +...+(n−i−1)di+1≡j−(n−i)a
因为f[i][j]代表的是组合数量,因为j-(n-i)a是已经确定的数值,所以变化的数量在前面的和里面,可以推出
所以
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
−
a
(
n
−
i
)
]
f[i][j] = f[i-1][j-a(n-i)]
f[i][j]=f[i−1][j−a(n−i)]
同理,如果选-b,
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
+
b
(
n
−
i
)
]
f[i][j] = f[i-1][j+b(n-i)]
f[i][j]=f[i−1][j+b(n−i)]
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
+
b
(
n
−
i
)
]
+
f
[
i
−
1
]
[
j
−
a
(
n
−
i
)
]
f[i][j] = f[i-1][j+b(n-i)]+f[i-1][j-a(n-i)]
f[i][j]=f[i−1][j+b(n−i)]+f[i−1][j−a(n−i)]
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010,MOD = 100000007;
int f[N][N]; //设f[i][j]为前i个数的总和模n余数为j的集合数
int get_mod(int a,int b) //求a除以b的正余数
{
return (a%b+b) % b;
}
int main()
{
int n,s,a,b;
cin >> n >> s >> a >> b;
f[0][0] = 1;
for(int i=1; i<n; ++i){
for(int j=0; j<n; ++j){
f[i][j] = ( f[i-1][get_mod(j-a*(n-i),n)] + f[i-1][get_mod(j+b*(n-i),n)] ) % MOD;
}
}
cout<<f[n-1][get_mod(s,n)]<<endl;
return 0;
}
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010,MOD = 100000007;
int f[N][N]; //设f[i][j]为前i个数的总和模n余数为j的集合数
int get_mod(int a,int b) //求a除以b的正余数
{
return (a%b+b) % b;
}
int main()
{
int n,s,a,b;
cin >> n >> s >> a >> b;
f[0][0] = 1;
for(int i=1; i<n; ++i){
for(int j=0; j<n; ++j){
f[i][j] = ( f[i-1][get_mod(j-a*(n-i),n)] + f[i-1][get_mod(j+b*(n-i),n)] ) % MOD;
}
}
cout<<f[n-1][get_mod(s,n)]<<endl;
return 0;
}