观察这个数列:
1 3 0 2 -1 1 -2 …
这个数列中后一项总是比前一项增加2或者减少3,且每一项都为整数。
栋栋对这种数列很好奇,他想知道长度为 n 和为 s 而且后一项总是比前一项增加 a 或者减少 b 的整数数列可能有多少种呢?
输入格式
共一行,包含四个整数 n,s,a,b,含义如前面所述。
输出格式
共一行,包含一个整数,表示满足条件的方案数。
由于这个数很大,请输出方案数除以 100000007 的余数。
数据范围
1≤n≤1000,
−1e9≤s≤1e9,
1≤a,b≤1e6
输入样例:
4 10 2 3
输出样例:
2
样例解释
两个满足条件的数列分别是2 4 1 3和7 4 1 -2。
题解:
化简等式:
设第一个数是x, d等于a或b)
x, x + d1, x + d1 + d2 … (x +…+ d(n – 1)) == s
=> (n) * x + (n – 1) * d1 + (n – 2) * d2 … 1 * d(n – 1) = s
=> x = (s – (n – 1) * d1 – (n – 2) * d2 … 1 * d(n – 1)) / n
由于 x 只要是整数就行, 所以只要 (s – (n – 1)d1 – (n – 2)d2 … 1xd(n – 1))是 n 的倍数就行, 也就是 s % n == ( (n – 1)d1 + (n – 2)d2 … 1xd(n – 1) ) % n
这里有个结论: 两个整数数 a, b. 如果 a % k == b % k, 那么说明 abs(a – b)一定是n的倍数。 蓝桥杯也出一个相关的题目 -> k倍区间
dp分析
常见疑惑: 为什么是状态计算是f[i][j] = f[i – 1][j – (n – i) * a] + f[i – 1][j + (n – i)b]
- 看到这里, 你要理解,等式左边f[i][j]中的 j是第i个选择的是a或b的序列的和对 n 取模后的余数, 这个序列里面是不包含初始值x的, 这也是为什么 i是[1,n),只循环了n – 1次,
(一共n个数, 我们只需要选n – 1次) - 观察等式
(n - 1) * d1 + (n - 2) * d2 ... 1 * d(n - 1) % n = j
可以发现 从左往右 第 i 项(不含第i项)的系数是 (n – i), 那么前 i 项的和应该是 j – (n – i)a 或 j + (n – i)b(d是a或-b)
要理解转移这个思想, 从 i – 1, j – (n – i) * a 、 j + (n – i)b 转移到f[i][j]
ac代码👇
#include
using namespace std;
const int N = 1e3 + 10, MOD = 100000007;
int f[N][N];
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
机房租用,北京机房托管,大带宽租用,IDC机房服务器主机租用托管-价格及服务咨询 www.e1idc.net