(公式可能渲染得比較慢,這時候,你只須要,放在那邊加載一會就行了qwq)html
遞歸定義斐波那契數列爲 $$ F_{n}=F_{n-1}+F_{n-2} \quad (n \geq 2) $$ 特別地,$F_0=0,F_1=1$。node
令 $\phi=\frac{1+\sqrt 5}2$,$\bar\phi=\frac{1-\sqrt 5}2$,那麼 $$ \boldsymbol{F_n=\frac 1{\sqrt 5}(\phi^n-\bar\phi^n)} $$c++
考慮利用通項公式計算斐波那契數列的第 $n$ 項模 $p$ 意義下的值。git
當 $5$ 是模 $p$ 的二次剩餘時,咱們能夠考慮直接用 Cipolla's algorithm 算出 $5$ 的二次剩餘;不然咱們能夠考慮用擴展數域來計算,即由於 $\sqrt5 \not \in \mathbb F_p$,因此咱們相似定義複數,定義擴展數域 $\mathbb F_p(\sqrt 5)$ 爲 $$ \mathbb F_p(\sqrt 5)={a+b\sqrt 5\arrowvert a,b\in \mathbb F_p} $$ 這種定義方法在下面研究模 $p$ 意義下的循環節也是有涉及到的。算法
擴展數域的加減乘除是很顯然的(用複數類比便可),相對於矩陣乘法,用擴展數域計算的優越性就是常數小。數組
設生成函數 $$ \mathcal F(x)=\sum_{n=0}^{+\infty}F_nx^n $$ 由遞推公式能夠獲得 $$ \mathcal F(x)=x^2\mathcal F(x)+x\mathcal F(x)+x $$ 解得 $$ \mathcal F(x)=\frac{x}{1-x-x^2} \tag 1 $$ 爲了將它表示爲級數形式,咱們考慮利用一個常見的生成函數來進行轉化 $$ \frac{1}{1-x}=\sum_{n=0}^{+\infty}x^n \tag2 $$ 假設咱們能夠將 $\mathcal F(x)$ 分解爲下面的形式,那麼利用就能夠很容易得得出級數表示,進而直接得出通項 $$ \mathcal F(x)=\frac{A}{1-\alpha x}+\frac{B}{1-\beta x}=\frac{A+B-x(A\beta+B\alpha)}{(1-\alpha x)(1-\beta x)} \tag 3 $$ 由 $(1)$ 可知轉化成這種形式應當是可行的,具體地,咱們能夠獲得下面的方程組 $$ \begin{cases} A+B-x(A\beta+B\alpha)=x\ (1-\alpha x)(1-\beta x)=1-x-x^2 \end{cases} $$ 不可貴到其中一組解: $$ \begin{cases} A=\frac{1}{\sqrt 5}\ B=-\frac{1}{\sqrt 5}\ \alpha=\frac{1+\sqrt 5}{2}=\phi\ \beta=\frac{1-\sqrt 5}{2}=\bar\phi \end{cases} $$ 所以將這些量代入 $(3)$,能夠獲得 $$ \mathcal F(x)=\frac{1}{\sqrt 5}(\frac{1}{1-\phi x}-\frac{1}{1-\bar \phi x}) $$ 利用 $(2)$ 能夠獲得 $$ \mathcal F(x)=\frac{1}{\sqrt 5}\sum_{n=0}^{+\infty}(\phi^n-\bar\phi^n)x^n $$ 即 $$ F_n=\frac 1{\sqrt 5}(\phi^n-\bar\phi^n) $$函數
直接代入能夠獲得 $$ \phi^2=\phi+1\ \bar\phi^2=\bar\phi+1 $$ 直接代入還能夠獲得 $$ F_0=\frac{1}{\sqrt 5}(\phi^0-\bar\phi^0)\ F_1=\frac{1}{\sqrt 5}(\phi^1-\bar\phi^1) $$ 假設對於 $0 \leq i \leq n$,都有 $F_i=\frac 1{\sqrt 5}(\phi^i-\bar\phi^i)$,那麼考慮對於 $n+1$ 就有 $$ \begin{aligned} F_{n+1} &=F_n+F_{n-1}\ &= \frac{1}{\sqrt 5}\left((\phi^n+\phi^{n-1})-(\bar\phi^n+\bar\phi^{n-1})\right)\ &= \frac{1}{\sqrt 5}\left(\phi^{n-1}(\phi+1)-\bar\phi^{n-1}(\bar\phi+1)\right)\ &= \frac{1}{\sqrt 5}\left(\phi^{n+1}-\bar\phi^{n+1}\right) \end{aligned} $$優化
本部分參考資料:https://www.math.arizona.edu/~ura-reports/071/Campbell.Charles/Final.pdfui
咱們能夠發現,在模意義下,斐波那契的第 $n+1$ 項 $F_{n+1}$ 僅取決於 $(F_{n-1}\bmod p,F_{n}\bmod p)$,不難發現這個二元組有 $p^2$ 種取值。所以,在模意義下,斐波那契數列必定會最終產生循環,即當 $n$ 足夠大時,必定會出現 $F_{n}=F_{n-k}$ 的狀況。this
而且根據遞推式,咱們有 $F_n=F_{n+2}-F_{n+1}$,也就是說,$F_{n+1}$ 和 $F_{n+2}$ 的前面一項必定是 $F_n$。
經過這兩個事實,咱們能夠知道,斐波那契數列在模意義下必定會產生循環節,而且必定是純循環的,即循環節的開始必定是 $F_0$ 和 $F_1$。所以,咱們就能夠定義斐波那契數列模意義下的最小循環節,並探究有關性質。
定義斐波那契數列模 $p$ 意義下的最小循環節 $\pi(p)$ 爲 $$ \pi(p)=\min{m \arrowvert \begin{cases}F_m\equiv 0\pmod p\F_{m+1}\equiv 1\pmod p\end{cases},m\in\mathbb N^*} $$
同時不可貴到 $$ \left{\begin{array}{l} {F_{k} \equiv 0 \pmod p} \ {F_{k+1} \equiv 1\pmod p} \end{array} \Leftrightarrow \pi(p) | k\right. \tag 4 $$
$\pi(p)$ 又被稱爲皮薩諾週期。
$\textbf{Theorem 1.}$ 對於質數 $p$ 和一個正整數 $a$,若 $a \equiv 1\pmod p$,則對於任意天然數 $n$,都有 $$ a^{p^n}\equiv 1 \pmod{p^{n+1}} $$
**證實:**考慮概括法證實,這個命題顯然對 $n=0$ 成立。假設對 $n$ 有 $a^{p^n}\equiv 1 \pmod{p^{n+1}}$ 成立,那麼咱們證實對 $n+1$ 也有 $a^{p^{n+1}}\equiv 1 \pmod{p^{n+2}}$ 成立。 $$ \begin{aligned}a^{p^{n+1}}&=\left(a^{p^{n}}\right)^p\&=(1+kp^{n+1})^p\&=1+(kp^{n+1})^{p}+\sum_{i=1}^{p-1}\binom{p}{i}(kp^{n+1})^i\end{aligned} $$ 由於 $\binom{p}{i}=\frac{p!}{i!(p-i)!}$,而且由於 $p$ 是質數,因此 $p|\binom{p}{i} (1<i<p)$。因此 $$ p^{n+2}|\binom{p}{i}(kp^{n+1})^i\ (1<i<p) $$
所以
$$ a^{p^{n+1}}\equiv 1 \pmod{p^{n+2}} $$
證畢。
$\textbf{Corollary 1.}$ 對於一個質數 $p$,若 $m$ 爲斐波那契數列模 $p$ 意義下的一個週期,那麼對於任意正整數 $k$,都有 $$ \phi^{mp^{k-1}}\equiv \bar\phi^{mp^{k-1}}\equiv 1\pmod {p^k} $$ **證實:**因爲 $$ F_m\equiv\frac{\phi^m-\bar\phi^m}{\sqrt 5}\equiv0\pmod p $$ 因此 $$ \phi^m\equiv\bar\phi^m\pmod p $$ 又由於 $$ F_{m+1}\equiv F_1\pmod p $$ 因此 $$ \phi^{m+1}-\bar\phi^{m+1}-\phi+\bar\phi\equiv \phi(\phi^m-1)-\bar\phi(\bar\phi^m - 1) \equiv ( \phi-\bar\phi)(\phi^m-1) \equiv 0 \pmod p $$ 故 $$ \phi^m\equiv\bar\phi^m\equiv 1\pmod p $$ 結合 $\text{Theorem 1}$,咱們能夠獲得 $$ \phi^{mp^{k-1}}\equiv \bar\phi^{mp^{k-1}}\equiv 1\pmod {p^k} $$ 證畢。
之因此要證實 $\text{Corollary 1}$,是爲了將模數爲 $p^k(p \text{ is a prime})$ 的狀況轉化爲模數爲 $p$ 的狀況,便於處理。
具體地,咱們有下面這個定理。
$\textbf{Theorem 2.}$ 對於質數 $p$,知足對於任意正整數 $k$,都有 $\pi(p^k)|p^{k-1}\cdot\pi(p)$。
**證實:**令 $m=\pi(p)$,那麼容易獲得 $$ F_{m p^{k-1}} \equiv \frac{\phi^{m p^{k-1}}-\bar{\phi}^{m p^{k-1}}}{\sqrt{5}} \equiv \frac{1-1}{\sqrt{5}} \equiv 0\pmod {p^k} $$
以及 $$ F_{m p^{k-1}+1} \equiv \frac{\phi^{m p^{k-1}+1}-\bar{\phi}^{m p^{k-1}+1}}{\sqrt{5}} \equiv \frac{\left(\phi^{m p^{k-1}}\right) \phi-\left(\bar{\phi}^{m p^{k-1}}\right) \bar{\phi}}{\sqrt{5}} \equiv \frac{\phi-\bar{\phi}}{\sqrt{5}} \equiv 1 \pmod {p^k} $$ 證畢。
$\textbf{Theorem 3.}$ 對於任意正整數 $n$,考慮 $n$ 的惟一分解形式 $n=\prod_{i=1}^s p_i^{k_i}$,那麼有 $$ \pi(n)=\mathrm{lcm}\left(\pi(p_1^{k_1}),\pi(p_2^{k_2}),\cdots,\pi(p_s^{k_s})\right) $$ 同時 $$ \pi(n)\ |\ \mathrm{lcm}\left(p_1^{k_1-1}\cdot \pi(p_1),p_2^{k_2-1}\cdot \pi(p_2),\cdots,p_s^{k_s-1}\cdot \pi(p_s)\right) $$ **證實:**設 $m=\pi(n)$,$m_i=\pi(p_i^{k_i})$,那麼 $$ \left{\begin{array}{l} {F_{m} \equiv 0\pmod n} \ {F_{m+1} \equiv 1\pmod n} \end{array}\right. $$ 又由中國剩餘定理,能夠知道 $$ \forall i, \left{\begin{array}{l} {F_{m} \equiv 0\pmod {p_i^{k_i}}} \ {F_{m+1} \equiv 1\pmod {p_i^{k_i}}} \end{array}\right. \tag 5 $$ 又由於 $$ \forall i, \left{\begin{array}{l} {F_{m_i} \equiv 0\pmod {p_i^{k_i}}} \ {F_{m_i+1} \equiv 1\pmod {p_i^{k_i}}} \end{array}\right. $$ 由 $(4)$ 可知式 $(5)$ 成立當且僅當 $\forall i,m_i|m$,那麼知足條件的最小的 $m$ 即爲 $\mathrm{lcm}(m_1,m_2,\cdots,m_s)$。
證畢。
前置知識:
經過 $\text{Theorem 3}$,咱們能夠將通常的模數轉化爲素數來處理,如今就只須要考慮素數的狀況。
一個不可避免的問題是,$\sqrt 5$ 在某些模數下多是沒有意義的,咱們須要進行一些討論。
在下面的討論中,咱們默認 $p \neq 2, p \neq 5$,而且 $p$ 是素數。這兩個數的狀況特殊討論。
在數論中,若存在整數 $x$ 知足 $x^2\equiv a\pmod p$,則稱 $a$ 是模 $p$ 的二次剩餘,不然是二次非剩餘。在本文中,咱們只關心 $5$ 是不是 $p$ 的二次剩餘,這涉及到 $\sqrt 5$ 是否在模 $p$ 下有意義。
利用二次互反律,咱們能夠知道
$$ \left(\frac{5}{p}\right)=\left(\frac{p}{5}\right)(-1)^{\left(\frac{5-1}{2}\right)\left(\frac{p-1}{2}\right)}=\left(\frac{p}{5}\right)\left((-1)^{\frac{p-1}{2}}\right)^{2}=\left(\frac{p}{5}\right) $$
而且
$$ \left(\frac{p}{5}\right)=\left{\begin{array}{ll} {\left(\frac{1}{5}\right)=1} & {\Leftrightarrow p \equiv 1\pmod 5} \ {\left(\frac{2}{5}\right) \equiv 2^{\frac{5-1}{2}} \equiv-1\pmod 5} & {\Leftrightarrow p \equiv 2\pmod 5} \ {\left(\frac{3}{5}\right) \equiv 3^{\frac{5-1}{2}} \equiv-1\pmod 5} & {\Leftrightarrow p \equiv 3\pmod 5} \ {\left(\frac{4}{5}\right)=\left(\frac{2^{2}}{5}\right)=1} & {\Leftrightarrow p \equiv 4\pmod 5} \end{array}\right. $$
所以 $5$ 是模 $p$ 的二次剩餘當且僅當 $p\equiv \pm 1\pmod 5$,$5$ 是模 $p$ 的二次非剩餘當且僅當 $p\equiv \pm 2\pmod 5$。
下面咱們就分這兩種狀況,給出兩個定理,解決斐波那契數列模質數意義下的循環節問題。
$\textbf{Theorem 4.}$ 若 $p$ 爲奇素數而且 $p\equiv \pm 1\pmod 5$,那麼 $\pi(p)|m-1$。
**證實:**由於 $p\equiv \pm 1\pmod 5$,因此 $5$ 是模 $p$ 的二次剩餘,所以 $\phi,\bar\phi\in \mathbb F_p$。應用費馬小定理,咱們能夠獲得 $$ \phi^{p-1}\equiv\bar\phi^{p-1}\equiv 1\pmod p $$ 因此 $$ \begin{aligned}&F_{p-1}\equiv \frac{\phi^{p-1}-\bar\phi^{p-1}}{\sqrt 5}\equiv 0\pmod p\&F_{p}\equiv \frac{\phi^p-\bar\phi^p}{\sqrt 5}\equiv\frac{\phi-\bar\phi}{\sqrt 5}\equiv 1\pmod p\end{aligned} $$ 根據 $(4)$ 咱們獲得 $\pi(p)|m-1$。證畢。
$\textbf{Theorem 5.}$ 若 $p$ 爲奇素數而且 $p\equiv \pm 2\pmod 5$,那麼 $\pi(p)|2m+2$,而且 $\frac{2m+2}{\pi(p)}$ 是奇數。
**證實:**由於 $p\equiv \pm 2\pmod 5$,因此 $5$ 是模 $p$ 的非二次剩餘,所以 $\phi,\bar\phi\not \in \mathbb F_p$,爲了方便,咱們在一個擴展的數域計算,使得 $\phi,\bar\phi$ 有意義。具體地,咱們有以下擴展的數域 $$ \mathbb F_p(\sqrt 5)={a+b\sqrt 5\arrowvert a,b\in \mathbb F_p} $$ 由於 $5^{\frac{p-1}2}\equiv -1\pmod p$,因此在模意義下 $$ \begin{aligned}\phi^{p}&=\left(\frac{1+\sqrt{5}}{2}\right)^{p} \&=\left(\frac{1}{2}\right)^{p}\left(1+\sqrt{5}\right)^{p}\&=\frac{1}{2}\left(1+\sqrt5^p+\sum_{i=1}^{p-1}\binom{p}{i}\sqrt5^i\right)\&=\frac{1}{2}\left(1+\sqrt5^p\right)\&=\frac{1}{2}\left(1+5^{\frac{p-1}{2}} \sqrt{5}\right) \&=\frac{1}{2}(1-\sqrt{5}) \&=\bar{\phi}\end{aligned} $$ 同理 $\bar\phi^p=\phi$。
所以咱們有 $$ \begin{aligned}&F_{p} \equiv \frac{\phi^{p}-\bar{\phi}^{p}}{\sqrt{5}} \equiv \frac{\bar{\phi}-\phi}{\sqrt{5}} \equiv-1\pmod p\&F_{p+1} \equiv \frac{\phi^{p+1}-\bar{\phi}^{p+1}}{\sqrt{5}} \equiv \frac{\phi^{p} \phi-\bar{\phi}^{p} \bar{\phi}}{\sqrt{5}} \equiv \frac{\bar{\phi} \phi-\phi \bar{\phi}}{\sqrt{5}} \equiv 0\pmod p\&F_{p+2}\equiv F_{p+1}+F_p\equiv -1\pmod p\end{aligned} $$ 所以由 $(4)$ 可知 $\pi(p)\nmid p+1$。而且咱們有 $$ \begin{aligned}&F_{2p+1} \equiv \frac{\phi^{2 p+1}-\bar{\phi}^{2 p+1}}{\sqrt{5}} \equiv \frac{\left(\phi^{p}\right)^{2} \phi-\left(\bar{\phi}^{p}\right)^{2} \bar{\phi}}{\sqrt{5}}\equiv \frac{\bar{\phi}^{2} \phi-\phi^{2} \bar{\phi}}{\sqrt{5}} \equiv \phi \bar{\phi}\left(\frac{\bar{\phi}-\phi}{\sqrt{5}}\right)\equiv 1\pmod p\&F_{2 p+2} \equiv \frac{\phi^{2 p+2}-\bar{\phi}^{2 p+2}}{\sqrt{5}} \equiv \frac{\left(\phi^{p}\right)^{2} \phi^{2}-\left(\bar{\phi}^{p}\right)^{2} \bar{\phi}^{2}}{\sqrt{5}} \equiv \frac{\bar{\phi}^{2} \phi^{2}-\phi^{2} \bar{\phi}^{2}}{\sqrt{5}} \equiv 0 \pmod p\&F_{2 p+3} \equiv F_{2 p+1}+F_{2 p+2} \equiv 1\pmod p\end{aligned} $$ 所以由 $(4)$ 可知 $\pi(p)|2p+2$,而且由於 $\pi(p)\nmid p+1$,因此 $\frac{2m+2}{\pi(p)}$ 是奇數。證畢。
綜合上述定理,對於任意模數 $n(n>1)$,考慮 $n$ 的惟一分解形式 $n=\prod_{i=1}^s p_i^{k_i}$。那麼斐波那契數列模 $n$ 意義下的最小循環節 $$ \boldsymbol{\pi(n)\ |\ \mathrm{lcm}\left(g(p_1)p_1^{k_1-1},g(p_2)p_2^{k_2-1},\cdots,g(p_s)p_s^{k_s-1}\right)} $$ 其中對於素數 $p$ $$ g(p)=\begin{cases}3 &p=2\20 &p=5\p-1 &p\equiv\pm1\pmod 5\2p+2 &p\equiv\pm2\pmod 5\end{cases} $$ 其中 $2$ 和 $5$ 的狀況比較特殊,是另外討論的結果。
若是要求最小週期,能夠先算出 $\mathrm{lcm}\left(g(p_1)p_1^{k_1-1},g(p_2)p_2^{k_2-1},\cdots,g(p_s)p_s^{k_s-1}\right)$,這個複雜度是質因數分解的複雜度。而後再枚舉約數就能夠知道最小週期了,驗證能夠經過矩陣乘法。
在通常應用中,一般只須要求出一個循環節,$\mathrm{lcm}\left(g(p_1)p_1^{k_1-1},g(p_2)p_2^{k_2-1},\cdots,g(p_s)p_s^{k_s-1}\right)$ 這個值在多數狀況下已經夠用了。
具體證實請見:Pisano_period
只考慮 $\mathrm{lcm}\left(g(p_1)p_1^{k_1-1},g(p_2)p_2^{k_2-1},\cdots,g(p_s)p_s^{k_s-1}\right)$ 的上界,咱們發現,上界主要取決於 $g(p)$,所以咱們觀察 $g(p)$。發現這個循環節變大的關鍵在於 $2$ 和 $5$,出現質因子 $2$ 帶來的貢獻是 $\times \frac{3}{2}$,出現質因子 $5$ 的貢獻是 $\times 4$。
所以,咱們發現形如 $n=2\times 5^r(r \in \mathbb N^*)$ 的循環節均爲 $6n$,而且能夠證實 $$ \boldsymbol{\pi(n)\leq 6n} $$ 具體證實在此不展開,根據 $g(p)$ 的式子分類討論也可得出這個結論。
這種作法是比較常見的,應用也十分普遍,可是前提是肯定了循環節的上界,以及轉移矩陣可逆纔有辦法使用。
考慮矩陣乘法求斐波那契數列第 $n$ 項的過程,用矩陣表示能夠寫成 $$ \begin{aligned} \begin{pmatrix} F_n & F_{n-1} \end{pmatrix} &= \begin{pmatrix} F_{n-1} & F_{n-2} \end{pmatrix} \times \begin{pmatrix} 1 & 1\ 1 & 0\ \end{pmatrix} \ &= \begin{pmatrix} F_{1} & F_{0} \end{pmatrix} \times \begin{pmatrix} 1 & 1\ 1 & 0\ \end{pmatrix}^{n-1} \end{aligned} $$ 記 $T=\begin{pmatrix}1 & 1\1 & 0\\end{pmatrix}$,那麼求循環節至關於求模意義下,知足 $T^m\equiv I \pmod p$ 的正整數 $m$。
套用 $\mathrm{BSGS}$ 算法的流程,在轉移矩陣 $T$ 可逆的前提下,咱們設 $m=iS-j(i\geq 1,0<j \leq S)$,因而方程能夠寫成 $$ T^{iS}\times T^{-j}\equiv I\pmod p $$ 即 $$ T^{iS}\equiv T^{j}\pmod p $$ 假設肯定的循環節上界爲 $q$,那麼取 $S=\sqrt q$,預處理其中一邊,存到哈希表內,而後查詢便可。時間複雜度 $\mathcal O(\sqrt q)$。
以斐波那契數列爲例,一樣咱們須要知道循環節的上界纔有辦法處理。由於 $\pi(p) \leq 6p$,能夠認爲是 $\mathcal O(p)$ 的。
若是每次隨機兩個數 $0 \leq i,j \leq 6p$,而後判斷是否有 $(F_i,F_{i+1})\equiv (F_j,F_{j+1})\pmod p$,若是有的話,那麼循環節就爲 $\lvert i-j\rvert$ 的約數。可是這樣作的指望次數是 $\mathcal O(p)$ 的,沒有什麼優點。
咱們考慮每次隨機兩個的機率過小,可是假設咱們不斷隨機,每次隨機三個、四個……機率就比較大了。更形式化地,假設咱們在一個 $n$ 個元素的集合內每次隨機選取一個元素,一共隨機 $m$ 次,存在兩次選取的元素相同的機率隨 $m$ 的增長是呈平方級別增長的,具體的分析能夠看 Birthday Problem——Wikipedia。
若是咱們改變一下算法流程,每次新隨機一個數 $x$,將 $(F_x\bmod p,F_{x+1}\bmod p)$ 的信息存到哈希表中,並查詢哈希表內以前有沒有相同的 $(F_y\bmod p,F_{y+1}\bmod p)$。能夠證實,這樣作的指望次數是 $\mathcal O(\sqrt p)$ 的。
實際狀況須要取上界爲 $12p$ 來保證指望次數。代碼能夠看參考博文。
求 $F_n\bmod p$,其中 $F_n$ 是斐波那契數列的第 $n$ 項。
$1 \leq p<2^{31}$,$n \leq 10^{3\cdot 10^7}$
時空限制:$\texttt{1s/512MB}$
直接用 $\mathcal O(\log n)$ 級別的作法都是不可行的,通常的解決方法就是找到循環節,而後計算 $n$ 在模循環節長度意義下的值,時間複雜度就能夠優化到 $\mathcal O(\log p)$ 了:
來源:FJWC2020 Day4T1
定義 $g_0=a,g_1=b$,$g_n=3g_{n-1}-g_{n-2}(n\geq 2)$。
定義 $f_{n,0}=n$,$f_{n,k}=f_{g_n,k-1}$。
給定 $a,b,n,k,p$,請你求出 $f_{n,k}\bmod p$。
$n,p\leq 10^9$,$k \leq 100$,數據組數 $T \leq 1000$。
時空限制:$\texttt{1s/512MB}$
能夠觀察到 $g_n=F_{2n}b-F_{2(n-1)}a$,其中 $F$ 是斐波那契數列,特別地,咱們定義 $F_{-1}=1,F_{-2}=-1$。
顯然 $g_n$ 的增加速度極快,要求 $g_{g_{g_{\dots g_n}}}$(共 $k$ 個 $g$),顯然不能用 $g_n$ 的真實值計算。能夠考慮用計算循環節的方式來處理。在每一層都計算一下當前模數的循環節,那麼下面一層的模數就用計算的循環節來代替。
矩陣 BSGS 的時間複雜度沒法承受,考慮利用前面推出的結論
$$ \pi\left(\prod_{i=1}^sp_i^{k_i}\right)=\mathrm{lcm}\left(g(p_1)p_1^{k_1-1},g(p_2)p_2^{k_2-1},\cdots,g(p_s)p_s^{k_s-1}\right) $$
能夠證實,不斷迭代下去,循環節的大小不會很大,具體地,咱們考慮當前循環節長度中質因子 $2,3,5$ 的次數,若它們的次數都足夠,那麼根據
$$ g(p)=\begin{cases}3 &p=2\20 &p=5\p-1 &p\equiv\pm1\pmod 5\2p+2 &p\equiv\pm2\pmod 5\end{cases} $$
咱們發現循環節就不會再增加了。
因而每一層計算一次循環節,而後遞歸下去便可。
#include <bits/stdc++.h> template <class T> inline void read(T &x) { static char ch; while (!isdigit(ch = getchar())); x = ch - '0'; while (isdigit(ch = getchar())) x = x * 10 + ch - '0'; } template <class T> inline void putint(T x) { static char buf[25], *tail = buf; if (!x) putchar('0'); else { for (; x; x /= 10) *++tail = x % 10 + '0'; for (; tail != buf; --tail) putchar(*tail); } } typedef long long s64; s64 mod; int a, b, n, K, p; inline s64 plus(s64 x, s64 y) { x += y; return x >= mod ? x - mod : x; } typedef long double ld; inline s64 qmul(s64 a, s64 b) { s64 res = a * b - (s64)((ld)a / mod * b + 1e-8) * mod; return res < 0 ? res + mod : res; } struct mat { int r, c; s64 a[2][2]; mat(){} mat(int _r, int _c): r(_r), c(_c) {memset(a, 0, sizeof(a));} inline mat operator * (const mat &rhs) const { mat res(r, rhs.c); for (int i = 0; i < r; ++i) for (int k = 0; k < c; ++k) for (int j = 0; j < rhs.c; ++j) res.a[i][j] = plus(res.a[i][j], qmul(a[i][k], rhs.a[k][j])); return res; } inline mat operator ^ (s64 p) const { mat res(r, c), x = *this; res.init(); for (; p; p >>= 1, x = x * x) if (p & 1) res = res * x; return res; } inline void init() { for (int i = 0; i < r; ++i) a[i][i] = 1; } }T(2, 2), F0(1, 2); inline s64 calc_G(s64 n, s64 p) { if (n == 0) return a % p; else if (n == 1) return b % p; mod = p; T.a[0][0] = 3 % mod, T.a[0][1] = 1 % mod; T.a[1][0] = mod - 1, T.a[1][1] = 0; F0.a[0][0] = b % mod, F0.a[0][1] = a % mod; return (F0 * (T ^ (n - 1))).a[0][0]; } const int MaxN = 1e6 + 5; int pri[MaxN], n_pri; inline s64 lcm(s64 a, s64 b) { return a / std::__gcd(a, b) * b; } inline void sieve_init(int n = 1000000) { static bool sie[MaxN]; for (int i = 2; i <= n; ++i) { if (!sie[i]) pri[++n_pri] = i; for (int j = 1; j <= n_pri && pri[j] * i <= n; ++j) { sie[i * pri[j]] = true; if (i % pri[j] == 0) break; } } } inline s64 getp(s64 p) { if (p == 2) return 3; else if (p == 3) return 8; else if (p == 5) return 20; else if (p % 5 == 1 || p % 5 == 4) return p - 1; else return 2 * p + 2; } inline s64 find_period(s64 p) { s64 x = p; s64 res = 1; for (int i = 1; x > 1 && i <= n_pri && 1LL * pri[i] * pri[i] <= p && pri[i] <= x; ++i) if (x % pri[i] == 0) { x /= pri[i]; s64 cur = getp(pri[i]); while (x % pri[i] == 0) x /= pri[i], cur *= pri[i]; res = lcm(res, cur); } if (x > 1) res = lcm(res, getp(x)); return res; } inline s64 calc(s64 n, int k, s64 p) { if (k == 0) return n % p; return calc_G(calc(n, k - 1, find_period(p)), p); } int main() { sieve_init(); int orzcx; read(orzcx); while (orzcx--) { read(a), read(b), read(n), read(K), read(p); if (p == 1) { puts("0"); continue; } putint(calc(n, K, p)); putchar('\n'); } return 0; }
$$ \boldsymbol{F_{n-1}F_{n+1}-F_{n}^2=(-1)^n} \tag 6 $$ 有一個比較簡潔的證實: $$ F_{n-1} F_{n+1}-F_{n}^{2}=\operatorname{det}\left(\begin{array}{cc} {F_{n+1}} & {F_{n}} \ {F_{n}} & {F_{n-1}} \end{array}\right)=\operatorname{det}\left(\begin{array}{ll} {1} & {1} \ {1} & {0} \end{array}\right)^{n}=\left(\operatorname{det}\left(\begin{array}{ll} {1} & {1} \ {1} & {0} \end{array}\right)\right)^{n}=(-1)^{n} $$
$$ \boldsymbol{F_{n+m}=F_mF_{n+1}+F_{m-1}F_n} \tag 7 $$
不妨設 $F_n=a$,$F_{n+1}=b$,那麼能夠概括證實 $F_{n+m}=F_{m-1}a+F_{m}b$,原式得證。
注意到將 $m=n$ 代入原式能夠獲得另一個美妙的性質 $$ \boldsymbol{F_{2n}=F_n(F_{n-1}+F_{n+1})} $$
注意到咱們能夠擴展斐波那契數列,對於 $F_n(n<0)$,咱們能夠將遞推公式反向 $F_n=F_{n+2}-F_{n+1}$。所以該性質沒有限制 $n,m$ 的正負性。咱們稱這種數列叫廣義斐波那契數列。
而且有一個頗有意思的性質 $$ \boldsymbol{F_{-n}=(-1)^{n-1}F_n} $$ 由概括法很容易能夠證實,這裏就不展開。
這個附加性質啓發咱們定義類斐波那契數列,對於一個數列 $G$,若 $G_0=a,G_1=b$,而且數列知足遞推關係式 $$ G_n=G_{n-1}+G_{n-2} $$ 則稱 $G$ 是類斐波那契數列,而且有 $$ G_n=F_{n-1}\cdot a+F_{n}\cdot b $$ 證實能夠考慮概括法。類斐波那契數列也有部分斐波那契數列的性質,具體狀況能夠具體分析。
首先咱們有個前綴和公式 $$ \boldsymbol{F_1+F_2+\cdots+F_n=F_{n+2}-1} \tag 8 $$ 證實方法:$\sum_{i=1}^nF_i=\sum_{i=1}^n(F_{i+2}-F_{i+1})=F_{n+2}-1$。
還有一些相似的性質,好比偶數項求和、奇數項求和 $$ \begin{aligned} &F_1+F_3+\cdots+F_{2n-1}=F_{2n}\ &F_{2}+F_{4}+\cdots +F_{2n}=F_{2n+1}-1 \end{aligned} $$ 證實也是寫成差的形式。
$$ \boldsymbol{F_{1}^{2}+F_{2}^{2}+\cdots+F_{n}^{2}=F_{n} F_{n+1}} \tag 9 $$
有一個很是巧妙的幾何證實,能夠看下面這張圖:
<img src="https://cdn.jsdelivr.net/gh/cyx0406/uploads/img_2020/20200202200848.png" style="zoom:67%;" />
固然也能夠概括:
顯然對於 $n=1$ 結論成立。
假設有 $\sum_{i=1}^{n-1}F_i^2=F_{n-1}F_n$ 成立,那麼 $$ \sum_{i=1}^nF_i^2=F_{n-1}F_n+F_n^2=F_nF_{n+1} $$
$$ \boldsymbol{\mathrm{gcd}(F_n,F_{n+1})=1} \tag{10} $$
證實也能夠概括:
顯然對於 $n=1$ 結論成立。
假設有 $\mathrm{gcd}(F_{n-1},F_n)=1$ 成立,那麼 $$ \mathrm{gcd}(F_n,F_{n+1})=\mathrm{gcd}(F_n,F_{n+1}-F_n)=\mathrm{gcd}(F_n,F_{n-1})=1 $$
$$ \boldsymbol{\mathrm{gcd}(F_n,F_m)=F_{\mathrm{gcd}(n,m)}} \tag{11} $$
這個性質的證實須要用到展轉相除法的一些性質:
不妨假設 $n<m$,由 $(7)$ 能夠推出 $$ \begin{aligned} \mathrm{gcd}(F_n,F_m)&=\mathrm{gcd}(F_n,F_{n+(m-n)})\ &=\mathrm{gcd}(F_n,F_{m-n-1}F_n+F_{m-n}F_{n+1})\ &=\mathrm{gcd}(F_n,F_{m-n}F_{n+1}) \end{aligned} $$
由 $(11)$ 可知 $\mathrm{gcd}(F_n,F_{n+1})=1$,因此 $$ \mathrm{gcd}(F_n,F_m)=\mathrm{gcd}(F_n,F_{m-n})(m>n) \tag{12} $$
根據 $(12)$ 遞歸下去就是展轉相減法,最終會獲得的形式應當就是 $$ \mathrm{gcd}(F_n,F_m)=F_{\mathrm{gcd}(n,m)} $$
這個性質還能夠導出其餘性質: $$ \boldsymbol{n|m \Leftrightarrow F_n|F_m}\tag{13} $$ 證實只要套用 $(11)$ 便可。
$$ \boldsymbol{2F_n=F_{n+1}+F_{n-2}} $$
代入 $F_{n+1}=F_{n}+F_{n-1}$ 和 $F_{n-2}=F_{n}-F_{n-1}$ 能夠輕鬆驗證。這個性質在某些題目中會有些用。
給定一棵 $n$ 個結點的有根樹,每一個點有點權,初始值爲 $0$,接下來有 $m$ 次操做,每次操做爲下面兩種之一:
- 給定 $x,k$,將屬於 $x$ 子樹的每一個點 $y$ 的點權加上 $F_{k+dis(x,y)}$,其中 $F$ 是斐波那契數列。
- 給定 $x,y$,詢問路徑 $(x,y)$ 上的點權和 $\bmod 10^9+7$。
$n,m \leq 10^5,0 \leq k\leq 10^{18}$。
時空限制:$\texttt{1s/512MB}$。
根據 $(7)$ 能夠獲得 $$ F_{k+\mathrm{dep}{y}-\mathrm{dep}{x}}=F_{k-\mathrm{dep}x}F{\mathrm{dep}y-1}+F{k-\mathrm{dep}x-1}F{\mathrm{dep}_y} $$ 用樹鏈剖分+線段樹打標記解決,注意 $k-\mathrm{dep}_x$ 多是負數,根據廣義斐波那契數列的定義,預處理這個範圍的 $F_x$ 便可。
能夠處理出樹鏈剖分完每一個前綴的 $\sum_{i=1}^xF_{\mathrm{dep}x-1}$ 和 $\sum{i=1}^xF_{\mathrm{dep}_x}$,便於打標記。
時間複雜度爲 $\mathcal O(n\log^2n)$。
#include <bits/stdc++.h> typedef long long s64; template <class T> inline void read(T &x) { static char ch; static bool opt; while (!isdigit(ch = getchar()) && ch != '-'); x = (opt = ch == '-') ? 0 : ch - '0'; while (isdigit(ch = getchar())) x = x * 10 + ch - '0'; if (opt) x = ~x + 1; } inline bool getopt() { static char ch; while ((ch = getchar()) != 'U' && ch != 'Q'); return ch == 'U'; } const int MaxNV = 1e5 + 5; const int MaxNE = MaxNV << 1; const int MaxNode = MaxNV << 2; const int mod = 1e9 + 7; inline void add(int &x, const int &y) { x += y; if (x >= mod) x -= mod; if (x < 0) x += mod; } struct matrix { int r, c; int a[3][3]; matrix(){} matrix(const int &x, const int &y): r(x), c(y) { memset(a, 0, sizeof(a)); } inline void clear() { memset(a, 0, sizeof(a)); } inline void init() { memset(a, 0, sizeof(a)); for (int i = 1; i <= r; ++i) a[i][i] = 1; } inline matrix operator * (const matrix &rhs) const { matrix res(r, rhs.c); for (int i = 1; i <= r; ++i) for (int j = 1; j <= rhs.c; ++j) for (int k = 1; k <= c; ++k) add(res.a[i][j], 1LL * a[i][k] * rhs.a[k][j] % mod); return res; } inline matrix operator ^ (s64 p) const { matrix x = *this, res(r, c); res.init(); for (; p; p >>= 1, x = x * x) if (p & 1) res = res * x; return res; } }A(1, 2), T(2, 2); int ect, adj[MaxNV], to[MaxNE], nxt[MaxNE]; int n, m, f_neg[MaxNV], f_pos[MaxNV], dis1[MaxNV], dis2[MaxNV]; int fa[MaxNV], son[MaxNV], sze[MaxNV], dep[MaxNV], pos[MaxNV], idx[MaxNV], top[MaxNV], totpos; int tag1[MaxNode], tag2[MaxNode]; int sum1[MaxNode], sum2[MaxNode]; #define lc (x << 1) #define rc (x << 1 | 1) #define trav(u) for (int e = adj[u], v; v = to[e], e; e = nxt[e]) inline void addEdge(const int &u, const int &v) { nxt[++ect] = adj[u], adj[u] = ect, to[ect] = v; } inline int getfib(const s64 &x) { if (x <= 0) return f_neg[-x]; if (x < MaxNV) return f_pos[x]; return (A * (T ^ (x - 1))).a[1][1]; } inline void dfs1(const int &u) { dep[u] = dep[fa[u]] + 1; sze[u] = 1; // son[u] = top[u] = 0; trav(u) if (v != fa[u]) { fa[v] = u; dfs1(v); sze[u] += sze[v]; if (sze[v] > sze[son[u]]) son[u] = v; } } inline void dfs2(const int &u) { if (son[u]) { idx[pos[son[u]] = ++totpos] = son[u]; top[son[u]] = top[u]; dfs2(son[u]); } trav(u) if (v != son[u] && v != fa[u]) { idx[pos[v] = ++totpos] = v; top[v] = v; dfs2(v); } } inline void upt(const int &x) { add(sum1[x] = sum1[lc], sum1[rc]); add(sum2[x] = sum2[lc], sum2[rc]); } inline void add_node(const int &x, const int &l, const int &r, const int &val1, const int &val2) { // printf(":%d %d %d %d %d:%d\n", x, l, r, val1, val2, dis1[r] - dis1[l - 1]); add(sum1[x], 1LL * (dis1[r] - dis1[l - 1]) * val1 % mod); add(tag1[x], val1); add(sum2[x], 1LL * (dis2[r] - dis2[l - 1]) * val2 % mod); add(tag2[x], val2); } inline void dnt(const int &x, const int &l, const int &r) { if (tag1[x] || tag2[x]) { int mid = l + r >> 1; add_node(lc, l, mid, tag1[x], tag2[x]); add_node(rc, mid + 1, r, tag1[x], tag2[x]); tag1[x] = tag2[x] = 0; } } inline void modify(const int &x, const int &l, const int &r, const int &u, const int &v, const int &val1, const int &val2) { if (u <= l && r <= v) { add_node(x, l, r, val1, val2); return; } dnt(x, l, r); int mid = l + r >> 1; if (u <= mid) modify(lc, l, mid, u, v, val1, val2); if (v > mid) modify(rc, mid + 1, r, u, v, val1, val2); upt(x); } inline int query(const int &x, const int &l, const int &r, const int &u, const int &v) { if (u <= l && r <= v) { int res = sum1[x] + sum2[x]; if (res >= mod) res -= mod; return res; } dnt(x, l, r); int mid = l + r >> 1, res = 0; if (u <= mid) add(res, query(lc, l, mid, u, v)); if (v > mid) add(res, query(rc, mid + 1, r, u, v)); return res; } inline int path_query(int u, int v) { int res = 0; while (top[u] != top[v]) { if (dep[top[u]] < dep[top[v]]) std::swap(u, v); add(res, query(1, 1, n, pos[top[u]], pos[u])); u = fa[top[u]]; } if (dep[u] > dep[v]) std::swap(u, v); add(res, query(1, 1, n, pos[u], pos[v])); return res; } int main() { A.a[1][1] = T.a[1][1] = T.a[1][2] = T.a[2][1] = 1; read(n), read(m); for (int i = 2; i <= n; ++i) { int u, v; read(u), read(v); addEdge(u, v); addEdge(v, u); } dfs1(1); pos[1] = idx[1] = top[1] = totpos = 1; dfs2(1); f_neg[0] = 0, f_neg[1] = 1; for (int i = 2; i <= n; ++i) add(f_neg[i] = f_neg[i - 2], -f_neg[i - 1]); f_pos[1] = f_pos[2] = 1; for (int i = 3; i < MaxNV; ++i) add(f_pos[i] = f_pos[i - 1], f_pos[i - 2]); for (int i = 1; i <= n; ++i) { add(dis1[i] = dis1[i - 1], getfib(dep[idx[i]])); add(dis2[i] = dis2[i - 1], getfib(dep[idx[i]] - 1)); } bool opt; int x; s64 y; while (m--) { opt = getopt(), read(x), read(y); if (opt) modify(1, 1, n, pos[x], pos[x] + sze[x] - 1, getfib(y - dep[x] + 1), getfib(y - dep[x])); else printf("%d\n", path_query(x, y)); } return 0; }
給定 $n,k$,令 $A(i_1,i_2,\cdots ,i_k)=F_{1+\sum_{i=1}^k(i_k-1)}$,其中 $F$ 是斐波那契數列,求 $$ \sum_{1 \leq i_1,i_2,\cdots,i_k \leq n}A(i_1,i_2,\cdots,i_k) \bmod 10^9+7 $$ $n,k \leq 10^9$,數據組數 $T \leq 100$。
時空限制:$\texttt{1s/512MB}$。
先從 $k=1$ 開始考慮,根據 $(8)$ 咱們能夠獲得 $\sum_{i=1}^nF_i=F_{n+2}-F_2$,所以 $k=1$ 的答案能夠表示爲斐波那契數列的兩項相減的形式。
再考慮 $k=2$,這時候至關於算這個式子 $$ \sum_{t=0}^{n-1}\sum_{i=t+1}^{t+n}F_i $$ 相似斐波那契數列前綴和的推導,咱們發現這個式子能夠寫成 $$ \sum_{t=0}^{n-1}F_{n+t+2}-F_{t+2} $$ 不難驗證數列 $F'n=F{n+\alpha+2}-F_{\alpha+2}$(其中 $\alpha$ 是常數)是一個類斐波那契數列(即知足斐波那契數列遞推式的數列,但對前兩項沒有要求)。所以這個式子還能夠寫成 $$ F'{n+2}-F'{2} $$ 以此類推,也就是說,前 $k$ 維的答案就能夠直接寫成一個類斐波那契數列的第 $n+2$ 項和第 $2$ 項相減的形式,這啓發咱們求出這個數列具體的形式。定義 $G_k(n)$ 表示在計算第 $k$ 維時對應的類斐波那契數列,形式化地,咱們有 $$ G_k(n)=\begin{cases} F_n & k=0\ G_{k-1}(n+1)-G_{k-1}(1) & k>0,n=0\ G_{k-1}(n+2)-G_{k-1}(2) & k>0,n=1\ G_{k}(n-1)+G_{k}(n-2) & k>0,n>1 \end{cases} $$ 由於 $G_k(n)$ 是類斐波那契數列,因此能夠證實 $G_k(n)$ 的本質實際上是考慮前 $k$ 維,而且 $i_k=n$ 的答案。
也就是說,當咱們要計算前 $k$ 維的答案 $S_k$ 時,其實就只要考慮 $$ S_k=\sum_{i=1}^nG_k(n)=G_k(n+2)-G_k(2) $$ 那麼如今的問題就很明顯了,咱們只須要求出 $G_k(n+2)$ 和 $G_k(2)$。具體地,咱們考慮到 $$ \begin{cases} G_k(0)=G_{k-1}(n+1)-G_{k-1}(1)\ G_k(1)=G_{k-1}(n+2)-G_{k-1}(2) \end{cases} $$ 考慮到類斐波那契數列的一個性質 $G_k(n)=F_{n-1}G_k(0)+F_nG_k(1)$,那麼上式能夠改寫成 $$ \begin{cases} G_k(0)=F_{n}G_{k-1}(0)+F_{n+1}G_{k-1}(1)-G_{k-1}(1)\ G_k(1)=F_{n+1}G_{k-1}(0)+F_{n+2}G_{k-1}(1)-G_{k-1}(1)-G_{k-1}(0) \end{cases} $$ 須要用到的斐波那契數能夠用矩乘預處理,上式也能夠用矩乘來遞推 $G_k(0),G_k(1)$。
那麼本題就作完了,時間複雜度就是 $\mathcal O(T\log n)$。
#include <bits/stdc++.h> const int mod = 1e9 + 7; int n, K; struct mat { int r, c; int a[5][5]; mat(){} mat(int n, int m): r(n), c(m) {memset(a, 0, sizeof(a));} inline void init() //初始化單位矩陣 { for (int i = 1; i <= r; ++i) a[i][i] = 1; } inline mat operator * (const mat &rhs) const //矩陣乘法 { mat res(r, rhs.c); for (int i = 1; i <= r; ++i) for (int k = 1; k <= c; ++k) for (int j = 1; j <= rhs.c; ++j) res.a[i][j] = (res.a[i][j] + 1LL * a[i][k] * rhs.a[k][j]) % mod; return res; } inline mat operator ^ (int p) const //矩陣快速冪 { mat res(r, c), x = *this; res.init(); for (; p; p >>= 1, x = x * x) if (p & 1) res = res * x; return res; } }; int main() { int TAT; std::cin >> TAT; while (TAT--) { scanf("%d%d", &n, &K); mat T(4, 4); T.a[1][1] = T.a[1][3] = T.a[2][2] = 1; T.a[2][4] = T.a[3][1] = T.a[4][2] = 1; mat F1 = mat(1, 4); F1.a[1][2] = 1, F1.a[1][3] = 1; mat F2 = F1; F1 = F1 * (T ^ n); F2 = F2 * (T ^ (n + 1)); int a1 = F1.a[1][1], b1 = F1.a[1][2]; int a2 = F2.a[1][1], b2 = F2.a[1][2]; //預處理類斐波拉契的n+1,n+2項對應的係數 mat F(1, 2); F.a[1][2] = 1; T = mat(2, 2); T.a[1][1] = a1, T.a[1][2] = a2 ? a2 - 1 : mod - 1; T.a[2][1] = b1 ? b1 - 1 : b1, T.a[2][2] = b2 ? b2 - 1 : b2; //經過獲得的係數得出轉移矩陣 F = F * (T ^ K); printf("%d\n", F.a[1][2]); } fclose(stdin); fclose(stdout); return 0; }
給定一棵 $n$ 個結點的有根樹,保證父親標號小於兒子,點 $i$ 有點權 $a_i$,進行 $m$ 次操做,共有 $4$ 種類型:
給定 $u,v$,將 $u$ 的父親改成 $v$,保證 $v<u$。
給定 $u,v,x$,將 $(u,v)$ 路徑上的點權所有改爲 $x$。
給出 $u$,詢問 $F(a_u)\bmod 998244353$。
給定 $u,v$,對於路徑 $u\to v$,假設路徑上的點權值分別是 $b_1,b_2,\cdots,b_k$,求 $$ \sum_{i=1}^k\sum_{j=i}^kF_{\sum_{p=i}^jb_p} \bmod 998244353 $$
其中 $F$ 是斐波那契數列。$n,m \leq 10^5,1 \leq a_i,x\leq 10^9$。
時空限制:$\texttt{2s/512MB}$。
考慮用通項公式處理斐波那契數列,在擴域 ${a+b\sqrt 5\arrowvert a,b\in\mathbb F_p}$ 下計算。
那麼 $$ F_{\sum_{p=i}^jb_p}=\frac{\prod_{p=i}^j\phi^{b_p}-\prod_{p=i}^j\bar\phi^{b_p}}{\sqrt 5} $$ 下面只考慮維護區間的全部子區間 $[l,r]$ 的 $\prod_{i=l}^r\phi^{b_i}$ 之和,$\bar\phi$ 的處理是相似的。
考慮用 LCT 實現時合併兩個區間的操做,以下圖:
修改父親直接用 LCT 實現就能夠了,考慮鏈覆蓋的操做,咱們須要在 LCT 上打標記。
只考慮覆蓋一整條鏈的操做對維護的信息的影響,以下圖:
由於要快速冪,因此時間複雜度爲 $\mathcal O(n\log^2n)$。
代碼暫時沒有。
給定一個長度爲 $n$ 的序列 $a_1,a_2,\cdots,a_n$,求 $$ \mathrm{lcm}(F_{a_1},F_{a_2},\cdots,F_{a_n})\bmod 10^9+7 $$ 其中 $F$ 是斐波那契數列。$n \leq 5\times 10^4,a_i\leq 10^6$。
時空限制:$\texttt{3s/128MB}$
咱們記這個序列構成的集合爲 $S$,對每一個質因子的冪進行 $\text{min-max}$ 容斥能夠獲得 $$ \mathrm{lcm}{S}=\prod_{T\subseteq S\T\neq \varnothing}\mathrm{gcd}{T}^{(-1)^{|T|+1}} $$ 而且根據 $(11)$,咱們能夠獲得 $$ \begin{aligned} \mathrm{lcm}{F_S}&=\prod_{T\subseteq S\T\neq \varnothing}\mathrm{gcd}{F_T}^{(-1)^{|T|+1}}\ &=\prod_{T\subseteq S\T\neq \varnothing}F_{\mathrm{gcd}{T}}^{(-1)^{|T|+1}}\ \end{aligned} $$
這個作法是從這裏學的:
這是一種比較巧妙的作法。
直接作仍是不太好作,對於帶 $\mathrm{gcd}$ 的這種式子,考慮反演會比較方便,假設咱們能構造一個序列 ${g_n}$ 知足 $$ F_n=\prod_{d|n}g_d $$ 那麼原式就能夠寫成 $$ \begin{aligned} \mathrm{lcm}{F_S}&=\prod_{T\subseteq S\T\neq \varnothing}\left(\prod_{d|\mathrm{gcd}{T}}g_d\right)^{(-1)^{|T|+1}}\ &=\prod_{d}g_d^{\sum_{T\subseteq S,T\neq \varnothing,d|\mathrm{gcd}{T}}(-1)^{|T|+1}} \end{aligned} $$ 注意 $g_d$ 上面指數的含義。注意到一個非空集合的大小爲奇數的子集個數,和大小爲偶數的子集個數是相等的(包括空集),所以指數部分能夠寫成 $$ \sum_{T\subseteq S,T\neq \varnothing,d|\mathrm{gcd}{T}}(-1)^{|T|+1}=[\exist a\in S,d|a] $$ 所以原式就能夠寫成 $$ \mathrm{lcm}{F_S} =\prod_{d}g_d[\exist a\in S,d|a] $$ 序列 ${g_n}$ 能夠直接硬算 $$ g_n=F_n\left(\prod_{d|n,d\neq n}g_d\right)^{-1} $$ 那麼這題就作完了,時間複雜度 $\mathcal O(n+m\log m)$,其中 $m=\max{a_i}$。
#include <bits/stdc++.h> template <class T> inline void read(T &x) { static char ch; while (!isdigit(ch = getchar())); x = ch - '0'; while (isdigit(ch = getchar())) x = x * 10 + ch - '0'; } template <class T> inline void relax(T &x, const T &y) { if (x < y) x = y; } const int mod = 1e9 + 7; const int MaxN = 5e4 + 5; const int MaxM = 1e6 + 5; int n, m; int a[MaxN]; bool vis[MaxM]; int f[MaxM], g[MaxM], t[MaxM]; inline void add(int &x, const int &y) { x += y; if (x >= mod) x -= mod; } inline int qpow(int x, int y) { if (x == 1) return 1; int res = 1; for (; y; y >>= 1, x = 1LL * x * x % mod) if (y & 1) res = 1LL * res * x % mod; return res; } int main() { read(n); for (int i = 1; i <= n; ++i) { read(a[i]); relax(m, a[i]); vis[a[i]] = true; } f[1] = 1; for (int i = 2; i <= m; ++i) add(f[i] = f[i - 1], f[i - 2]); for (int i = 1; i <= m; ++i) t[i] = 1; int ans = 1; for (int i = 1; i <= n; ++i) { g[i] = 1LL * f[i] * qpow(t[i], mod - 2); bool flg = false; if (vis[i]) flg = true; for (int j = i + i; j <= m; j += i) { t[j] = 1LL * t[j] * g[i] % mod; if (vis[j]) flg = true; } if (flg) ans = 1LL * ans * g[i] % mod; } std::cout << ans << std::endl; return 0; }
這是一種比較套路的作法。
能夠直接套用莫比烏斯反演,比較推得硬核一些: $$ \begin{aligned} \mathrm{lcm}{F_S}&=\prod_{T\subseteq S\T\neq \varnothing}F_{\mathrm{gcd}{T}}^{(-1)^{|T|+1}}\ &=\prod_{d}F_{d}^{\sum_{T\subseteq S,T\neq \varnothing}(-1)^{|T|+1}[\mathrm{gcd}{T}=d]} \end{aligned} $$ 把指數部分拿出來 $$ \begin{aligned} &\sum_{T\subseteq S,T\neq \varnothing}(-1)^{|T|+1}[\mathrm{gcd}{T}=d]\ =&\sum_{T\subseteq S,T\neq \varnothing\d|\gcd{T}}(-1)^{|T|+1}[\mathrm{gcd}\left{\frac{T}d\right}=1]\ =&\sum_{d|k}\left(\sum_{k|\gcd{T}}(-1)^{|T|+1}\right)\mu\left(\frac k d\right)\ =&\sum_{d|k}[\exist a\in S,k|a]\mu\left(\frac k d\right) \end{aligned} $$ 那麼指數部分能夠直接枚舉倍數算,這題就作完了。
這種作法的代碼暫時沒有。
參考:
- Zeckendorf's theorem——Wikipedia
- 《具體數學》(第2版,人民郵電出版社)P248~249
- LOJ 3184: 「CEOI2018」斐波那契表示法——Pinkrabbit
$\textbf{Zeckendorf's theorem:}$ 任何正整數 $n$ 均可以被表示爲若干項不一樣且不相鄰的斐波那契數之和,且表示方法是惟一的(不包括 $F_0$ 和 $F_1$)。形式化地,對於任意正整數 $n$,有且僅有一個知足 $c_1\geq 2$ 且 $c_i \geq c_{i-1}+2(i>1)$ 的正整數序列 ${c_k}$,使得 $$ n=\sum_{i=1}^kF_{c_i} $$ 而且咱們稱這種惟一的表示方法爲齊肯多夫表示(Zeckendorf representation)。
證實:這個定理分爲兩部分:存在一種齊肯多夫表示,而且表示方法是惟一的。
首先證實對於任意正整數 $n$,存在一種齊肯多夫表示。咱們能夠考慮概括證實,假設這個結論對於知足 $n <F_k$ 的正整數 $n$ 成立,那麼接下來咱們證實對於知足 $F_{k}\leq n < F_{k+1}$ 的正整數 $n$ 也是成立的。
咱們考慮在 $n$ 的齊肯多夫表示中加上 $F_k$,那麼考慮 $n-F_k<F_{k+1}-F_{k}=F_{k-1}$,所以 $n-F_k$ 的齊肯多夫表示中不包含 $F_{k-1}$,咱們直接用 $n-F_{k}$ 的齊肯多夫表示加上 $F_k$,就能獲得一個符合條件的表示。
接下來咱們證實這個齊肯多夫表示是惟一的。須要先有一個引理。
$\textbf{Lemma.}$ 對於知足最大的斐波那契數爲 $F_i$ 的一種齊肯多夫表示,這種表示的全部斐波那契數之和嚴格小於 $F_{i+1}$。 **證實:**引理的證實比較簡單,只要對 $F_i$ 進行概括,刪去 $F_i$ 後能夠獲得比它小的那些數的和嚴格小於 $F_{i-1}$,進而加上 $F_i$ 就嚴格小於 $F_{i+1}$。
接下來採用反證法,咱們假設有兩個不連續的斐波那契數的集合 $S,T(S\neq T)$,知足 $S$ 中的斐波那契數之和等於 $T$ 中的斐波那契數之和。去掉它們中的相同元素,分別獲得 $S'=S\setminus (S\cap T)$,$T'=T\setminus(S\cap T)$,根據 $S\neq T$ 以及集合中的數都是正的,$S',T'$ 應當均非空,而且由於去掉的是相同元素,兩個集合中的元素之和也應當相等。
假設 $S'$ 中的最大元素爲 $F_s$,而 $T'$ 中的最大元素爲 $F_t$。不失通常性地,咱們假設 $F_s>F_t$,那麼就有 $T'$ 中的元素之和 $<F_{t+1}\leq F_s$,所以就有兩個集合中的元素之和不相等,產生矛盾,故假設不成立。
結合兩個證實,咱們就能證實齊肯多夫定理。
齊肯多夫定理的證實過程也告訴了咱們如何構造一個正整數的齊肯多夫表示:每次找到當前小於等於 $n$ 的最大斐波那契數,而後從 $n$ 裏面減掉這個斐波那契數,重複執行這個過程直到 $n=0$。
齊肯多夫定理告訴咱們,任意正整數 $n$ 都有一個惟一的齊肯多夫表示法。任何有惟一性的表示方法都是是個數系,這樣一來,齊肯多夫定理就引導出斐波那契數系,咱們能夠將任何非負整數 $n$ 用 $0$ 和 $1$ 的一個序列表示,記 $$ n=(b_mb_{m-1}\cdots b_2)F\Leftrightarrow n=\sum{i=2}^mb_iF_i\ \ \ (b_i\in{0,1}) $$ 而且 $b$ 中不存在兩項 $b_i$ 和 $b_{i+1}$ 同爲 $1$。
給定一個正整數 $n$,求 $n$ 能寫成多少種不一樣的斐波那契數之和(不包括 $F_0$ 和 $F_1$)。
$n \leq 10^{18}$
時空限制:$\texttt{0.5s/128MB}$
咱們考慮先求出 $n$ 的齊肯多夫表示,這樣方便咱們處理。
能夠證實,一種斐波那契表示(即若干不一樣的斐波那契數之和)必定能夠用齊肯多夫表示經過若干次以下操做達到:假設當前的斐波那契數集合爲 $S$,找到一個知足 $F_i\in S\land F_{i-1},F_{i-2}\not\in S$ 的 $F_i$,而後令新的集合爲 $(S\setminus{F_i})\cup{F_{i-1},F_{i-2}}$。
證實只須要反過來考慮便可,咱們考慮每次在當前的斐波那契表示 $T$ 中找到一個知足 $F_i,F_{i+1}\in T,F_{i+2}\not \in T$ 的 $F_i$,而後令新的集合爲 $(T\setminus{F_i,F_{i-1}})\cup{F_{i+2}}$。容易發現若干次這樣的操做事後就能使集合不存在連續的兩個斐波那契數,獲得齊肯多夫表示,而且這個操做是上面的操做的反操做,因此結論成立。
所以咱們不妨這麼想,考慮 $n$ 的在斐波那契數系下的表示 10001000100
,咱們能夠將其分段爲 [1000][1000][100]
。由於咱們要求,將集合 $S$ 中的 $F_i$ 分解成 $F_{i-1}+F_{i-2}$ 時,必需要有 $F_{i-1},F_{i-2}\not \in S$,所以若 10000
變成了 01100
,後面只能分解較小的那個斐波那契數,即分解爲 01010
。
所以咱們發現,每一段之間大體是獨立的,咱們能夠考慮分階段 DP。可是有一種特殊狀況,[1000][1000][100]
若是最小的那段先分解成 [1000][1000][011]
,那麼中間這段能夠利用空出來的空位,使得多分解一個變成 [1000][0101][111]
。
所以咱們設 $f_{i,0/1}$ 表示考慮了齊肯多夫表示的前 $i$ 個斐波那契數,第 $i$ 個是否有分解的方案數。能夠發現,每一段內,能分解的老是當前最小的那個 1
,還要考慮 $i-1$ 是否分解帶來的空位影響。所以咱們有 $$ \begin{cases} f_{i,0}=f_{i-1,0}+f_{i-1,1}\ f_{i,1}=\lfloor\frac{c_i-c_{i-1}-1}{2}\rfloor f_{i-1,0}+\lfloor\frac{c_i-c_{i-1}}{2}\rfloor f_{i-1,1} \end{cases} $$ 邊界條件是 $c_0=0,f_{0,0}=1,f_{0,1}=0$,注意本題咱們須要將 $F_1=1,F_2=2$。
本題中的斐波那契數列的前兩項爲 $F_1=1,F_2=2$。
令 $X(p)$ 表示把 $p$ 表示爲若干個不一樣的斐波那契數的和的表示法數,兩種表示法不一樣當且僅當有一個斐波那契數是其中一個的項,而不是另外一個的項。
給定一個 $n$ 項正整數序列 $a_1,a_2,\cdots,a_n$,請你對於每一個 $1 \leq k \leq n$,求出 $X\left(\sum_{i=1}^kF_{a_i}\right)\bmod 10^9+7$。
$n \leq 10^5$,$a_i \leq 10^9$
時空限制:$\texttt{4s/256MB}$
考慮沿用上一題的 DP 思路,不過本題須要支持動態維護齊肯多夫表示,所以咱們須要考慮動態 DP。
考慮在上一題中,咱們能夠將轉移方程寫成矩陣的形式 $$ \begin{pmatrix}f_{i,0} & f_{i,1}\end{pmatrix}=\begin{pmatrix}f_{i-1,0} & f_{i-1,1}\end{pmatrix}\times \begin{pmatrix} 1 & \lfloor\frac{d_i-1}{2}\rfloor\ 1 & \lfloor\frac{d_i}{2}\rfloor \end{pmatrix} $$ 其中 $d_i=c_i-c_{i-1}$,即表示差分數組。假設齊肯多夫表示中有 $m$ 個數,答案即爲 $f_{m,0}+f_{m,1}$,其中 $$ \begin{pmatrix}f_{m,0} & f_{m,1}\end{pmatrix}= \begin{pmatrix}1 & 0\end{pmatrix}\times\prod_{i=1}^m\begin{pmatrix} 1 & \lfloor\frac{d_i-1}{2}\rfloor\ 1 & \lfloor\frac{d_i}{2}\rfloor \end{pmatrix} $$ 若是咱們能維護差分數組和對應的矩陣乘積,就能夠解決這題了。
那麼如今的問題是考慮加入一個 $F_x$ 後會有什麼影響,須要注意咱們必定要保證當前的齊肯多夫表示中不包含相同或連續的兩個斐波那契數。
首先咱們先考慮若當前 $x-1,x,x+1$ 三個位置均爲空,咱們直接將 $x$ 插入便可。不然就會使其餘位置也產生變化,須要仔細考慮。
發現產生的影響與 $0,1$ 的交替段密切相關,假設涉及的段的最低位的 $1$ 的位置爲 $l$,最高位的 $1$ 的位置爲 $r$($l-1 \leq x\leq r+1$),須要分狀況討論:(左邊低位,右邊高位)
上述分類討論的依據都是 $F_i=F_{i-1}+F_{i-2}$ 和 $2F_i=F_{i+1}+F_{i-2}$。
注意到上述討論存在遞歸操做,可是咱們能夠證實,遞歸操做的次數是常數次:咱們只考慮三種狀況之間的遞歸,若是遞歸到 $x-1,x,x+1$ 都爲空這種狀況顯然不要緊。第 $1$ 種狀況只可能遞歸到第 $2$ 種狀況,第 $3$ 種狀況只可能遞歸到第 $1$ 種,而第 $2$ 種狀況又不可能遞歸到其餘兩種。所以遞歸是常數次的。
所以能夠用 $\text{std::set}$ 維護極大的交替段,用 $\text{Splay}$ 維護差分序列、矩陣乘積。不難發現每次的操做次數都是均攤常數的,須要注意每一個操做可能產生的交替段的合併、刪除、分裂等細節。
在 $\text{Splay}$ 上面維護的時候有一些細節,若是用差分數組前綴和來表示每一個結點的位置編號是比較方便的,可是插入刪除的時候須要注意一些細節。或者直接存下位置編號,可是一整段編號加一的操做可能要用打標記實現。
時間複雜度 $\mathcal O(n \log n)$,用數組展開、循環展開等方式處理矩陣能夠大幅減少常數。
給定正整數 $k$,求用斐波那契數的和或差表示 $k$ 所須要的斐波那契數數量最小值。
$k \leq 4\times 10^7$
時空限制:$\texttt{1s/64MB}$
這題的貪心作法是這樣的:每次找到一個離 $k$ 最近的斐波那契數 $F_i$,令 $k\leftarrow|k-F_i|$,重複若干次直到 $k=0$。(即每次令 $k \leftarrow \min|k-F_i|$)
可是我在網上一直找不到比較好的證實,很是自閉QAQ。
首先有幾個性質:
存在最優方案,不會選擇重複的一項。
**證實:**由於咱們有 $2F_i=F_{i+1}+F_{i-2}$。
存在最優方案,不會選擇相鄰的兩項。
**證實:**經過討論能夠知道 $$ \begin{cases} +F_i+F_{i+1}=+F_{i+2}\ +F_i-F_{i+1}=-F_{i-1}\ -F_i+F_{i+1}=+F_{i-1}\ -F_i-F_{i+1}=-F_{i+2} \end{cases} $$
若當前 $F_i \leq k \leq F_{i+1}$,那麼存在最優方案,必定包含了 $F_i$ 或 $F_{i+1}$。
證實:反證法。假設不包含 $F_i$ 和 $F_{i+1}$。那麼根據不選相鄰和重複的原則,咱們能夠證實其餘部分的斐波那契數經過加減必定沒法湊到 $[F_i,F_{i+1}]$ 內的數。具體地,咱們有 $F_{i-1}+F_{i-3}+\cdots<F_i$ 成立,這是由於 $F_1+F_3+\cdots +F_{2n-1}=F_{2n}-1$ 和 $F_2+F_4+\cdots +F_{2n}=F_{2n+1}-1$(爲方便設 $F_1=1,F_2=2$)。
這樣一來,$F_1\sim F_{i-1}$ 的數裏選出來的和 $S<F_i$。一樣咱們若是用比 $F_{i+1}$ 大的數拿去減,也會遇到這種的狀況: $F_{i+2}-S>F_{i+1}$ ,而且由於不能選相鄰的,$F_{i+4}-F_{i+2}>F_{i+3}$ 也是沒用的。
所以咱們就證實了,存在最優方案,必定包含了 $F_i$ 或 $F_{i+1}$。
那麼接下來,咱們就獲得了,若當前 $F_{i}\leq k \leq F_{i+1}$,咱們必定要從 $F_i,F_{i+1}$ 中選一個。
接下來咱們概括證實,必定是選較近的那個斐波那契數:
至此咱們證實了貪心策略的正確性。
顯然,$k$ 每次至少減小一半,因此答案是 $\mathcal O(\log k)$ 級別的,這也是時間複雜度的級別。