题解 P1962 【斐波那契数列】
提供一种没有在题解区看到的打表做法,不使用矩阵乘法,但是比较锻炼观察能力。
虽然本质上是矩阵乘法的展开,但是换了一种理解的方式。
Update On 2022/11/23
修改代码中的小错误,并进一步优化方法和阅读体验。
首先,发现斐波那契数列性质
将计算看作是从
也就是说,我们依次计算
向后移动
由此可知,只有
注意,此时将
现在我们需要关注的是两个式子中
显然,
因为
我选择了
据此,我们可以计算得到所有
const ll mod = 1'000'000'007;
vector<ull> FibPow2, FibPow2Minus1;
ull n, f0, f1 = 1, nf0, nf1;
int main ()
{
std::cin.tie (nullptr), std::ios::sync_with_stdio (false);
for (ull i = 0; i <= 62; ++i)
{
FibPow2.emplace_back (f1), FibPow2Minus1.emplace_back (f0);
nf0 = f1 * f1 % mod + f0 * f0 % mod;
nf1 = f1 * f1 % mod + 2 * f0 * f1 % mod;
if (nf0 >= mod) nf0 -= mod;
if (nf1 >= mod) nf1 -= mod;
tie (f0, f1) = mkp (nf0, nf1);
}
cin >> n, f0 = 0, f1 = 1;
while (n)
{
static ll i; i = __lg (n & (-n));
nf0 = FibPow2Minus1[i] * f0 % mod + FibPow2[i] * f1 % mod;
nf1 = FibPow2[i] * f0 % mod + f1 * (FibPow2[i] + FibPow2Minus1[i]) % mod;
if (nf0 >= mod) nf0 -= mod;
if (nf1 >= mod) nf1 -= mod;
tie (f0, f1) = mkp (nf0, nf1);
n -= (1ll << i);
}
cout << f0 << endl;
return 0;
}
下附对第一个式子的三种证明方式。
证明方式 1
Consider the following problem: A person climbs up
n steps, by taking either one step, or two steps at a time. The total number of ways the person can climb up all then steps isF_{n+1} (Why?) Now consider climbingm+n−1 steps and split into the cases when the person lands on stepn and the cases when the person lands on stepn−1 and takes two steps at that point (and so does not land on stepn in those cases). These two cases cover all possibilities, and so we have:F_{m+n}=F_{n+1} \times F_{m}+F_{n} \times F_{m−1}
From StackExchange
考虑一个问题:爬楼梯,一次可以爬一步或者两步,那么爬上
考虑爬
- 经过第
n 级台阶,方案数为F_{n + 1} \times F_{m} 。 - 不经过第
n 级台阶,即经过第n - 1 级台阶并直接到第n + 1 级台阶,方案数为F_{n} \times F_{m - 1} 。
而总方案数为
证明方式 2
归纳法。
当
当
若已知条件如下:
则将两式相加,得到:
由于已证明
证明方式 3
我们知道,对于
得证。