首先,考虑把数列递推写作矩阵乘法的形式。
\[\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)\)
标签:log,线段,矩阵,pmatrix,区间,Fibonotci,乘法,CF575A From: https://www.cnblogs.com/jucason-xu/p/17620684.html