波动数列
波动数列
观察这个数列:
…
这个数列中后一项总是比前一项增加 或者减少 ,且每一项都为整数。
栋栋对这种数列很好奇,他想知道长度为 和为 而且后一项总是比前一项增加 或者减少 的整数数列可能有多少种呢?
输入格式
共一行,包含四个整数 ,含义如前面所述。
输出格式
共一行,包含一个整数,表示满足条件的方案数。
由于这个数很大,请输出方案数除以 的余数。
数据范围
,
,
输入样例:
4 10 2 3
输出样例:
2
样例解释
两个满足条件的数列分别是 和 。
解题思路
我们发现上面求和的等式有很多的变量,其中有无穷多种选法,每个都有两种选法。因此每一个不同的和每一组不同的都对应一种不同的方案。
我们可以发现这是一个等式,意味着可以去掉一个变量,即可以用其中的个变量去表示某一个变量(一个很重要的思想,我经常想不到)。由于可以取到很多值,所以我们可以用来表示。
因此可以表示为也就是说,当我们的都确定后,也是唯一确定的,是可以求出来的。
因此任何一个合法的序列都对应一组的取值,任何一组的取值都可以对应一个原来的序列,因此这是一一对应的关系。因此原序列所有不同的方案数就等于所有的合法取值的方案数。
因此等价于求所有满足要求的的取值的方案数。满足的要求有:只有两种取值方式;由于为整数,所以应该为的倍数。等价于要满足即
所以我们可以发现这是一个组合的问题,要求我们找到的一个组合,满足,类似于一个背包问题。
其中我们原本的式子为,我们可以变为,因为每个是等同的,每个都是取或,因此改变变量名称是不影响的。其实不进行变换也是可以的,变化后处理会方便点。
在状态计数中,最后一项是的所有方案这个集合中,对于前项有,因此有。同理可得到对于最后一项是的所有方案这个集合中,有。都可以从状态转移到。
AC代码如下:
1 #include <cstdio> 2 #include <algorithm> 3 using namespace std; 4 5 const int N = 1010, mod = 100000007; 6 7 int f[N][N]; 8 9 int main() { 10 int n, s, a, b; 11 scanf("%d %d %d %d", &n, &s, &a, &b); 12 13 f[0][0] = 1; // 初始化:一项都不选,总和是0的情况 14 for (int i = 1; i < n; i++) { 15 for (int j = 0; j < n; j++) { 16 f[i][j] = (f[i - 1][((j - i * a) % n + n) % n] + f[i - 1][(j + i * b) % n]) % mod; 17 } 18 } 19 20 printf("%d", f[n - 1][(s % n + n) % n]); 21 22 return 0; 23 }
参考资料
AcWing 1214. 波动数列(蓝桥杯C++ AB组辅导课):https://www.acwing.com/video/639/
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/15907623.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效