QOJ #1280.Fibonacci Partition
(为什么布置的作业题没有任何可见 AC 记录啊/kk)
拿下了 QOJ 上的用户首杀(同时目前也是 QOJ 可见的 submission 中唯一一个过掉这个题的,另一个是 vjudge 上我的提交)。
也许是这个题实在是太冷门了,但是从 Fibonacci-Lucas 数列的性质应用上是一道非常有意义的题。
题意
有一个 \(X\) 初始为 \(0\),进行 \(m\) 次操作。每次操作 \(X\leftarrow X+a\cdot F_b\),其中 \(b\) 为第 \(b\) 项 Fibonacci 数(这里的 Fibonacci 数列的定义与之前不同,\(F_1=1,F_2=2\))。每次操作后输出一个数 \(\lambda(X)\),其中 \(\lambda(x)\) 表示 \(x\) 最多可以表示成多少项不同的 Fibonacci 数之和。
\(|a|,b\le10^9,m\le5\times 10^4\)。
Sol
我们钦定 Fibonacci 数列为如下定义:
\[F_0 = 0, F_1=1,F_n=F_{n-1}+F_{n-2}\\ F_{-n}=(-1)^{n+1}F_n\\ n>0 \]这样的定义使得对于任意一个整数 \(x\) 都满足 \(F_x=F_{x-1}+F_{x-2}\),是一个十分优雅的扩展定义。
但是下文所有有关分解或者表示法都只包括 \(n>1\) 的项。
Zeckendorf theorem, Zeckendorf's representation
描述
任意正整数都有且仅有一种方法,可以表示成若干在数列上不连续的 Fibonacci 数的和,且不包括 \(F_1\)。
证明
存在性
通常的方法是使用一种构造方法。
令正整数为 \(x\)。
从大到小枚举每个 Fibonacci 数,如果 \(F_i\le x\),则 \(x\leftarrow x-F_i\)。
然后我们通过归纳来证明。
有命题:\(\forall x : x<F_i\),\(x\) 一定可以被 \(F_2,F_3,\cdots,F_{i-1}\) 中不连续的一些数表出。
假设此命题对于 \(i\) 即以前的数成立,目前我们有 \(F_i\le x<F_{i+1}\) 即 \(F_i\le x<F_i+F_{i-1}\)。
我们要将这个命题推向 \(i+1\),因此我们考虑 \(x'\leftarrow x-F_i\),因此得到 \(x'<F_{i-1}\),所以 \(x\) 可以表示为 \(F_i+x'\),而 \(x'\) 可以被表示为 \(F_2,F_3,\cdots,F_{i-2}\) 中不连续的数之和。
因此我们通过归纳证明出了存在性。
唯一性
仍然通过上述的构造方法证明。
考虑如果要不同于上述构造的结果,那么必定有一个 \(F_i\le x\) 的时刻并没有令 \(x\leftarrow x-F_i\),因此我们只需要证明 \(\forall x : x\ge F_i\) 一定不能被表示成 \(F_{i-1},F_{i-2},\cdots,F_2\) 中不连续的数的和即可。
那么可知的是,表示的最大数应该为 \(F_i+F_{i-2}+\cdots+[2\mid i]F_2+[2\nmid i]F_3\)。
会发现无论什么情况,只要把这个数加上 \(1\),就可以从最后一个数往前合并,最后得到 \(F_i\),因此上式应该等于 \(F_i-1\)。
因此 \(\forall x:x\ge F_i\) 一定不能被表示成 \(F_{i-1},F_{i-2},\cdots,F_2\) 中不连续的数的和。
\(\Box\)
性质
容易证明 Zeckendorf's representation 一定是项数最少的分解。
而且另外一件关于这道题的性质是从这个分解到题目要求的最大分解的转化。
我们可以发现如下的转化一定是满足题目要求的:
10000101001 (Zeckendorf's representation)
10011001001
11101001001
11101110001
11101110110 (Answer)
因此我们得到已知 Zeckendorf's representation 时的一个计算贡献的方法。
- 初始有 \(ans=0,las=1\),记 \(p_i\) 表示第 \(i\) 项分解。
- 对于 \(p_i - las < 3\) 时,\(las\leftarrow p_i,\; ans\leftarrow ans+1\)。
- 对于 \(p_i-las\ge 3\) 时,\(las\leftarrow p_i-1,ans\leftarrow ans+\left\lceil\frac{p_i-las}{2}\right\rceil\)。
Fibonacci-Lucas 数列
我们定义 Lucas 数列:
\[L_0=2,L_1=1,L_n=L_{n-1}+L_{n-2} \]结论
\[L_k\cdot F_n=F_{n+k}+(-1)^kF_{n-k} \]证明
归纳法证明。当 \(k=0,1\) 时显然成立。
假设结论在 \(\forall k' : k'<k\) 成立,则应有:
\[L_{k-1}\cdot F_n=F_{n+k-1}+(-1)^{k-1}F_{n-k+1} \\ L_{k-2}\cdot F_n=F_{n+k-2}+(-1)^{k-2}F_{n-k+2} \]直接将两式相加得到:
\[(L_{k-1}+L_{k-2})\cdot F_n=(F_{n+k-1}+F_{n+k-2})+(-1)^k(F_{n-k+2}-F_{n-k+1}) \\ L_k\cdot F_n=F_{n+k}+(-1)^k F_{n-k} \]\(\Box\)
用途
通过与 Zeckendorf's representation 相同的构造方法可以证明任何正整数都有 Lucas 数列上的分解(但不一定不连续)。记得只剩 \(L_0,L_1\) 时应该先考虑 \(L_0\) 再考虑 \(L_1\),因为 \(L_0>L_1\)。
因此题面中的 \(a\cdot F_b\) 可以转化成严格小于 \(2\log_2 a\) 项 Fibonacci 数之和,因此我们只需要维护 \(X\leftarrow X\pm F_y\) 即可。
数据结构维护
考虑维护 \(X\) 的 Zeckendorf's representation。
具体来说会发现不管是计算贡献还是加入,连续的 \(10\) 串形如 \(1010101\) 是会被连带影响的,因此使用 ODT 或普通平衡树维护即可。
发现可以分类讨论加入的情况:
- \(00101010100\to 01101010100\) 的情况可以得到 \(00000000010\)。
- \(00101010100\to 00101110100\) 的情况可以得到 \(00101000010\)。
- \(00101010100\to 00101020100\) 的情况可以得到 \(11111110100\) 进而得到 \(10010100010\)。
- 减法的 \(0(-1)00000\) 可以变成 \(1111\cdots 10(-1)\) ,因此往前找到第一个 \(1\) 去掉,这个 \(1\) 一定时存在的,然后把后面的连续的 \(1\) 变成 \(1010\cdots\) 的形态。
非常繁琐,因此实现较为复杂,应该有更好的实现。
时间复杂度 \(O(n\log_2^2 V)\),\(V\) 为值域。