CF575A - Fibonotci

首先,考虑把数列递推写作矩阵乘法的形式。

\[\begin{pmatrix} 0&1\\ s_n&s_{n+1} \end{pmatrix}\begin{pmatrix} F_{n+1}\\ F_{n} \end{pmatrix}=\begin{pmatrix} F_{n+2}\\ F_{n+1} \end{pmatrix}\]

这个是很明显的。

然后我妈发现,在大多数时候下,前面的矩阵都是固定循环的。只有在特殊的 \(s_j\) 生效时特殊。

我们发现,称在位置 \(i\) 贡献表示从 \((F_{i},F_{i+1})\) 推到 \((F_{i+1},F_{i+2})\),一个特殊的 \(s_j\) 只会在 \(j-1\) 处和 \(j\) 处进行贡献,也就是特殊点的个数是 \(O(m)\) 的。那么我们可以把 \([0,k-1]\) 用 特殊点断开。

除了特殊点以外的部分,都是一些连续段,如 \([l,r]\),这其中的所有 \(s\) 都是初始的 \(s\) 的循环。考虑如何处理。

方法一:线段树+矩阵快速幂。

我们考虑用线段树维护区间矩阵乘法。我们发现,当前的区间如果按照模 \(n\) 分段,可能是一个区间,也有可能是前缀、后缀带上中间若干个(可能是 \(0\) 个)完整的 \(0\sim n-1\) 循环。

我们发现,如果线段树维护区间矩阵乘法,就可以在 \(O(\log n)\) 的时间内完成区间、前缀、后缀的计算。而中间完整的循环明显可以对 \(0\sim n-1\) 矩阵的乘积做对应次数的矩阵快速幂解决。

但是如果朴素的线段树,复杂度来到 \(O(2^3(n+m)(\log n+\log k))\),如果实现的不好是会挂的。

这时候,我们发现,因为是静态的,所以我们可以使用 \(zkw\) 线段树的技巧,从下往上建树。同时,通过指针等技巧优化线段树和指针的对象调用。这样就可以比较轻松的写过。

方法二:倍增。

我们考虑倍增,预处理从 \([0,n-1]\) 的每一个位置,向后拓展 \(2^i\) 次的区间矩阵积。

然后我们把 \([x,y]\) 中的 \(x\) 平移到 \([0,n-1]\),直接调用倍增计算即可。

但是这个做法同样有一些要注意的,就是倍增的空间复杂度是 \(O(2^2 n\log k)\),乍看没问题,但是如果使用大量 vector 实现矩阵乘法就会挂掉。所以不应当贪图向量乘的一点便利,而应当直接将矩阵大小锁死在 \(2\times 2\)

时间复杂度 \(O((n+m)\log k)\)

posted @ 2023-08-10 16:17  jucason_xu  阅读(12)  评论(0编辑  收藏  举报