CCF-NOIP-2018 提高组(复赛)模拟试题 楼梯问题
[Question Description]
一层楼共有 n 级台阶,一次可以上至少 1 级但**不超过** m 级台阶,求有多少种不同的上楼方案数。由于结果可能很大,你只需要输出结果对 10,007 取模的值即可。
[Input Format]
输入数据只有一行,包含两个正整数 n 和 m。
[Output Format]
一个整数,表示所求结果对 10,007 取模的值。
[Sample Input 1]
4 3
[Sample Output 1]
7
[Sample Explanation 1]
共有 7 种不同的上楼梯方案,分别为:
- 第 1 步上 1 级,第 2 步上 1 级,第 3 步上 1 级,第 4 步上 1 级。
- 第 1 步上 1 级,第 2 步上 1 级,第 3 步上 2 级。
- 第 1 步上 1 级,第 2 步上 2 级,第 3 步上 1 级。
- 第 1 步上 2 级,第 2 步上 1 级,第 3 步上 1 级。
- 第 1 步上 2 级,第 2 步上 2 级。
- 第 1 步上 1 级,第 2 步上 3 级。
- 第 1 步上 3 级,第 2 步上 1 级。
[Sample Input 2]
1024 5
[Sample Output 2]
8590
[Data Scale And Agreement]
测试点编号 | n的规模 | m的规模 |
---|---|---|
1 | 1 ≤ \leq ≤n ≤ \leq ≤ 10 | m = 1 |
2 | 1 ≤ \leq ≤n ≤ \leq ≤ 10 | m = 1 |
3 | 1 ≤ \leq ≤n ≤ \leq ≤ 10 | m = 1 |
4 | 1 ≤ \leq ≤n ≤ \leq ≤ 10 | m = 1 |
5 | 1 ≤ \leq ≤n ≤ \leq ≤ 10 | m = 2 |
6 | 1 ≤ \leq ≤n ≤ \leq ≤ 10 | m = 2 |
7 | 1 ≤ \leq ≤n ≤ \leq ≤ 10 | m = 2 |
8 | 1 ≤ \leq ≤n ≤ \leq ≤ 10 | m = 2 |
9 | 1 ≤ \leq ≤n ≤ \leq ≤ 10 | m = 3 |
10 | 1 ≤ \leq ≤n ≤ \leq ≤ 10 | m = 3 |
11 | 1 ≤ n ≤ 10,000 | 2 ≤ m ≤ 10 |
12 | 1 ≤ n ≤ 10,000 | 2 ≤ m ≤ 10 |
13 | 1 ≤ n ≤ 100,000 | 2 ≤ m ≤ 10 |
14 | 1 ≤ n ≤ 100,000 | 2 ≤ m ≤ 10 |
15 | n = 233,333,333 | m = 5 |
16 | n = 666,666,666 | 2 ≤ m ≤ 5 |
17 | 1 1 1 ≤ n ≤ 1 0 9 10^9 109 | m=2 |
18 | 1 1 1 ≤ n ≤ 1 0 12 10^{12} 1012 | m=2 |
19 | 1 ≤ n ≤ 1 0 15 10^{15} 1015 | 2 ≤ m ≤ 10 |
20 | 1 ≤ n ≤ 1 0 18 10^{18} 1018 | 2 ≤ m ≤ 10 |
[Solution]
Emmm… 这道题被加强了又加强,开始是只能踏一或二步,后来可以踏一到m步,但数据没有这么变态,现在魔改数据真tm加强的炉火纯青
70~80分做法
可得递推式为 F n = F n − 1 + F n − 2 + . . . + F n − m F_n=F_{n-1}+F_{n-2}+...+F_{n-m} Fn=Fn−1+Fn−2+...+Fn−m
双重循环递推,第十五和十六测试点打表
100分做法
真牛逼
递推是不行滴
矩阵乘法好处大,谁说对了谁用它
构造矩阵加速递推,式子同上:
#include<bits/stdc++.h>
using namespace std;
const int mod=10007;
int n,m;
struct matrix
{
int mat[20][20];
matrix operator *(const matrix &B)
{
matrix res;
memset(res.mat,0,sizeof res.mat);
for(int i=1;i<=m;i++)
for(int j=1;j<=m;j++)
for(int k=1;k<=m;k++) res.mat[i][j]=(res.mat[i][j]+mat[i][k]*B.mat[k][j])%mod;
return res;
}
}tmp,ans;
matrix qpow(matrix a,int b)
{
matrix rs;
memset(rs.mat,0,sizeof rs.mat);
for(int i=1;i<=m;i++)
for(int j=1;j<=m;j++) if(i==j) rs.mat[i][j]=1;
while(b)
{
if(b&1) rs=rs*a;
a=a*a;
b>>=1;
}
return rs;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
tmp.mat[i][m]=1;
for(int i=2;i<=m;i++) tmp.mat[i][i-1]=1;
ans.mat[1][0]=1;
for(int i=1;i<=m;i++)
for(int j=0;j<i;j++) ans.mat[1][i]=ans.mat[1][i]+ans.mat[1][j]%mod;
ans=ans*qpow(tmp,n-1);
printf("%d",ans.mat[1][1]);
return 0;
}