1214. 波动数列
题目链接
1214. 波动数列
观察这个数列:
1 3 0 2 -1 1 -2 …
这个数列中后一项总是比前一项增加2或者减少3,且每一项都为整数。
栋栋对这种数列很好奇,他想知道长度为 n 和为 s 而且后一项总是比前一项增加 a 或者减少 b 的整数数列可能有多少种呢?
输入格式
共一行,包含四个整数 \(n,s,a,b\),含义如前面所述。
输出格式
共一行,包含一个整数,表示满足条件的方案数。
由于这个数很大,请输出方案数除以 \(100000007\) 的余数。
数据范围
\(1≤n≤1000,\)
\(−10^9≤s≤10^9,\)
\(1≤a,b≤10^6\)
输入样例:
4 10 2 3
输出样例:
2
样例解释
两个满足条件的数列分别是2 4 1 3和7 4 1 -2。
解题思路
dp
设初始值为 \(x\),\(d_i\) 为 \(a\) 或 \(-b\),则序列和为 \(nx+(n-1)d_1+(n-2)d_2+\dots +d_{n-1}=s\),则:\(x=\frac{s-((n-1)d_1+(n-2)d_2+\dots +d_{n-1})}{n}\),由于 \(x\) 为任意整数,所以 \(s\equiv (n-1)d_1+(n-2)d_2+\dots +d_{n-1}\bmod n\),然后进行dp:
-
状态表示:\(f[i][j]\) 前 \(i\) 个数余数为 \(j\) 的方案数
-
状态计算:\(f[i][j]=(f[i-1][((j-(n-i)*a)%n+n)%n]+f[i-1][((j+(n-i)*b)%n+n)%n])%mod\)
分析:即第 \(i\) 个数选 \(a\) 还是 \(-b\) -
时间复杂度:\(O(n^2)\)
代码
// Problem: 波动数列
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/1216/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=1005,mod=1e8+7;
int a,b,s,n,f[N][N];
int main()
{
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][((j-(n-i)*a)%n+n)%n]+f[i-1][((j+(n-i)*b)%n+n)%n])%mod;
cout<<f[n-1][(s%n+n)%n];
return 0;
}