凌乱的地下室 题解

· · 题解

题意

## 题目分析 老规矩 DP,$f_n$ 表示 $n$ 个物体放 $n$ 个格子的方案数。我们先看看 0 号物体放哪儿: - 0 号物体放 0 号格:剩下 $n - 1$ 个物体放 $n - 1$ 个格子,方案数 $f_{n-1}$。 - 0 号物体放 1 号格:此时 1 号物体必须放 0 号格(否则 0 号格就没东西可放了)。剩下 $n - 2$ 个物体放 $n - 2$ 个格子,方案数 $f_{n-2}$。 综上转移函数是 $f_n = f_{n - 1} + f_{n - 2}$。没错,此题本质就是 Fibonacci,快速求 Fibonacci 数列第 n 项一般采用矩阵法: $$\begin{bmatrix} f_{n+1} & f_n \\ f_n & f_{n-1} \end{bmatrix} = \begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix}^n$$ 问题在于这里 $n \leq 10^{1000}$。对于如何处理这么大的整数,每篇题解都是八仙过海各显神通。不过本蒟蒻还是想到一种更简单易行的方法(明明很简单为什么大佬们都没写呢)。 ## 核心内容 一句话概括:将快速幂从二进制扩展到十进制。原始的快速幂大家都理解了,在计算 $a^b$ 时: - 如果 $b$ 的第零个二进制位为 $1$,则这一位对结果的贡献为 $a$; - 如果 $b$ 的第一个二进制位为 $1$,则这一位对结果的贡献为 $a^2$; - 如果 $b$ 的第二个二进制位为 $1$,则这一位对结果的贡献为 $a^4$; - ...... 那何不将其扩展到十进制的形式? - 如果 $b$ 的个位为 $x$,则这一位对结果的贡献为 $a^x$; - 如果 $b$ 的十位为 $x$,则这一位对结果的贡献为 $a^{10x}$; - 如果 $b$ 的百位为 $x$,则这一位对结果的贡献为 $a^{100x}$; - ...... 这样做的好处是 $n$ 可以直接用字符串储存,而不需要使用高精度并将其转化为二进制了。 代码如下(只放核心的快速幂 $m^{pow}$ 部分,可以看到 $pow$ 是字符串而非整数): ``` matrix22 power(matrix22 m, std::string pow) { matrix22 result(1, 0, 0, 1); while(!pow.empty()) { int pow_bit = pow.back() - '0'; for(int i = 0; i < pow_bit; i++) result = result * m; m = m.pow10(); pow.pop_back(); } return result; } ```