2026年山东省小学组【信息学体验营】复赛真题及题解T2:小兔子爬楼梯
题目描述
森林学校里有一座 n n n 级的台阶,小兔子要跳上去。
它每一次跳跃,可以选择跳 1 1 1 级、 2 2 2 级、......、 m m m 级(每次跳的级数必须是整数,且在 1 1 1 到 m m m 之间)。
小兔子体力无限,他想尝试各种跳跃方案(跳完 n n n 级台阶的跳跃序列)。
但是,小兔子的老师说:"每一种跳跃方案中,至少要有一次跳的级数不少于 k k k( k ≤ m k\le m k≤m)级(称为'逆天一跳'),才算一种合格的跳跃方案"。
比如: n = 7 n=7 n=7, m = 5 m=5 m=5, k = 3 k=3 k=3,在以下跳跃方案中:
跳跃序列: 1 , 2 , 2 , 2 1,2,2,2 1,2,2,2 不是合格的跳跃方案;
跳跃序列: 1 , 3 , 3 1,3,3 1,3,3 是合格的跳跃方案;
跳跃序列: 1 , 4 , 2 1,4,2 1,4,2 与 1 , 5 , 1 1,5,1 1,5,1 都是合格的跳跃方案。
现在,小兔子想知道:一共有多少种不同的合格的跳跃方案 ,能恰好跳完 n n n 级台阶。
注意:跳跃序列顺序不同算不同的跳跃方案。比如 1 , 1 , 5 1,1,5 1,1,5 与 1 , 5 , 1 1,5,1 1,5,1 是两种不同的跳跃方案。
因为合格的跳跃方案可能太多了,答案要对 10 9 + 7 10^9+7 109+7 取模。
输入格式
一行三个整数: n , m , k n,m,k n,m,k。
输出格式
输出一个整数,表示符合条件的合格跳跃方案总数(对 10 9 + 7 10^9+7 109+7 取模)。
输入输出样例 1
输入 1
3 3 2
输出 1
3
输入输出样例 2
输入 2
4 3 2
输出 2
6
输入输出样例 3
输入 3
10000 100 60
输出 3
20640995
说明/提示
【样例 1 1 1 说明】
合格的跳跃方案有 3 3 3 种: 2 , 1 2,1 2,1; 1 , 2 1,2 1,2; 3 3 3。
【数据范围】
所有数据满足: 1 ≤ n ≤ 100000 1\le n\le 100000 1≤n≤100000, 1 ≤ m ≤ 100 1\le m\le 100 1≤m≤100, 1 ≤ k ≤ m 1\le k\le m 1≤k≤m。
| 测试点编号 | m m m | k k k | 特殊性质 |
|---|---|---|---|
| 1 ∼ 3 1\sim 3 1∼3 | = 2 =2 =2 | = 1 =1 =1 | 无 |
| 4 ∼ 9 4\sim 9 4∼9 | ≤ 100 \le 100 ≤100 | = 1 =1 =1 | 无 |
| 10 ∼ 20 10\sim 20 10∼20 | ≤ 100 \le 100 ≤100 | ≤ m \le m ≤m | 无 |
思路分析
题目要求计算所有跳跃序列中,至少有一次跳跃的级数不少于 (k) 的方案数。
这等价于:
总方案数 − 所有跳跃级数都小于 (k) 的方案数(即每次跳 (1\sim k-1) 级)。
设:
- fi:跳到第 i 级台阶的总方案数(每次可跳 1 ∼ m 1\sim m 1∼m 级)。
- gi:跳到第 i 级台阶且每一步都小于 k 的方案数(每次可跳 1 ∼ k − 1 1\sim k-1 1∼k−1 级)。
递推关系
-
初始:f0=1, g0=1。
-
对于 i ≥ 1 i\ge 1 i≥1:
f i = ∑ j = 1 m f i − j ( i − j ≥ 0 ) fi=\sum_{j=1}^{m} fi-j\quad (i-j\ge 0) fi=∑j=1mfi−j(i−j≥0)
g i = ∑ j = 1 k − 1 g i − j ( i − j ≥ 0 ) gi=\sum_{j=1}^{k-1} gi-j\quad (i-j\ge 0) gi=∑j=1k−1gi−j(i−j≥0)
当 k=1 时,gi 的求和范围为空,故 gi=0(i>0)。
-
答案:
ans = ( f n − g n ) m o d ( 10 9 + 7 ) \text{ans}=(fn-gn)\bmod (10^9+7) ans=(fn−gn)mod(109+7)
复杂度
- 时间复杂度: O ( n ⋅ m ) O(n\cdot m) O(n⋅m),最大约 10 7 10^7 107,可接受。
- 空间复杂度: O ( n ) O(n) O(n)。
代码实现
cpp
#include <bits/stdc++.h>
using namespace std;
const int MOD = 1000000007;
int n, m, k;
int main() {
cin >> n >> m >> k;
vector<long long> f(n + 1, 0), g(n + 1, 0);
// 1. 计算总方案数 f
f[0] = 1;
for (int i = 1; i <= n; ++i) {
long long sum = 0;
// 最后一步跳 j 级 (1 <= j <= m)
for (int j = 1; j <= m; ++j) {
if (i >= j) {
sum += f[i - j];
// 因为 sum 最多加 m 次,每次 < MOD,m <= 100,不会溢出 long long
}
}
f[i] = sum % MOD;
}
// 2. 计算不合格方案数 g:每一步都 < k
g[0] = 1;
for (int i = 1; i <= n; ++i) {
long long sum = 0;
// 最后一步只能跳 1 ~ k-1 级
for (int j = 1; j <= k - 1; ++j) {
if (i >= j) {
sum += g[i - j];
}
}
g[i] = sum % MOD;
}
// 3. 答案 = 总方案 - 不合格方案
long long ans = (f[n] - g[n]) % MOD;
if (ans < 0) ans += MOD; // 处理负数
cout << ans ;
return 0;
}
更多内容请关注专栏:信奥赛C++普及组csp-j初赛&复赛真题题解(持续更新): https://blog.csdn.net/weixin_66461496/category_12808781.html 点击跳转
【秘籍汇总】(完整csp信奥赛C++学习资料):
1、csp/信奥赛C++,完整信奥赛系列课程(永久学习):
https://edu.csdn.net/lecturer/7901 点击跳转

2、CSP信奥赛C++竞赛拿奖视频课:
https://edu.csdn.net/course/detail/40437 点击跳转

https://edu.csdn.net/course/detail/41081 点击跳转

3、csp信奥赛高频考点知识详解及案例实践:
CSP信奥赛C++动态规划:
https://blog.csdn.net/weixin_66461496/category_13096895.html点击跳转
CSP信奥赛C++标准模板库STL:
https://blog.csdn.net/weixin_66461496/category_13108077.html 点击跳转
信奥赛C++提高组csp-s知识详解及案例实践:
https://blog.csdn.net/weixin_66461496/category_13113932.html 点击跳转
4、csp信奥赛冲刺一等奖有效刷题题解:
信奥赛C++普及组CSP-J一等奖通关刷题题单及题解:
https://blog.csdn.net/weixin_66461496/category_12673810.html 点击跳转
信奥赛C++普及组csp-j初赛&复赛真题题解(持续更新): https://blog.csdn.net/weixin_66461496/category_12808781.html 点击跳转
信奥赛C++提高组csp-s初赛&复赛真题题解(持续更新):
https://blog.csdn.net/weixin_66461496/category_13125089.html 点击跳转
5、GESP C++考级真题题解:

GESP(C++ 一级+二级+三级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12858102.html 点击跳转

GESP(C++ 四级+五级+六级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12869848.html 点击跳转

GESP(C++ 七级+八级)真题题解(持续更新):
https://blog.csdn.net/weixin_66461496/category_13117178.html 点击跳转
· 文末祝福 ·
cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
cout<<"跟着王老师一起学习信奥赛C++";
cout<<" 成就更好的自己! ";
cout<<" csp信奥赛一等奖属于你! ";
return 0;
}
