1. 程式人生 > 實用技巧 >Solution -「國家集訓隊」「洛谷 P4451」整數的 lqp 拆分

Solution -「國家集訓隊」「洛谷 P4451」整數的 lqp 拆分

\(\mathcal{Description}\)

  Link.

  求

\[\sum_{m>0\\a_{1..m}>0\\a_1+\cdots+a_m=n}\prod_{i=1}^mf_{a_i} \]

  其中\(f_i\) 為 Fibonacci 數列第 \(i\) 項(\(f_0=0,f_1=1\)),答案對 \(10^9+7\) 取模。

  \(n\le10^{10^4}\)

\(\mathcal{Solution}\)

  記 \(F(x)\)\(\{f\}\) 的 OGF,首先來推導 \(F(x)\)。根據定義 \(f_{n+2}=f_{n+1}+f_n\),可以發現經過適當位移,\(F(x)\)

能推出其自身而解出表示式。具體地:

\[\begin{aligned} &~~~~~~~~~~~xF(x)+F(x)=\frac{1}xF(x)-1\\ &\Rightarrow~~~~(x+1-\frac{1}x)F(x)=-1\\ &\Rightarrow~~~~F(x)=\frac{x}{1-x-x^2} \end{aligned} \]

  令 \(n\) 的答案為 \(g_n\)\(g_0=1\)。簡單 DP 得出遞推式:

\[g_n=\sum_{i=1}^nf_ig_{n-i} \]

  顯然的卷積關係。令 \(\{g\}\) 的 OGF 為 \(G(x)\)

,則:

\[\begin{aligned} G(x)&=1+G(x)F(x)\\ &=\frac{1}{1-F(x)}\\ &=\frac{1-x-x^2}{1-2x-x^2} \end{aligned} \]

  提出一些常數:

\[\Rightarrow~~~~G(x)=1-\frac{x}{x^2+2x-1} \]

  我們想求 \(g_n\),即 \([x^n]G(x)\),就得把上式最後一項分式結構配湊成等比數列求和的形式。首先暴力因式分解 \(x^2+2x-1\),用求根公式求出其兩根:

\[x_{1,2}=\frac{-2\pm2\sqrt{2}}{2}=-1\pm\sqrt2 \]

  所以 \(x^2+2x-1=(x-x_1)(x-x_2)\)。代入 \(G(x)\) 的表示式:

\[\begin{aligned} G(x)&=1-\frac{x}{(x-x_1)(x-x_2)}\\ &=1-\frac{x}{x_1-x_2}\left(\frac{1}{x-x_1}-\frac{1}{x-x_2} \right)\\ &=1-\frac{x}{x_1-x_2}\left(\frac{1}{x_2}\sum_{i=0}^{+\infty}\frac{x^i}{x_2^i}-\frac{1}{x_1}\sum_{i=0}^{+\infty}\frac{x^i}{x_1^i} \right)\\ &=1-\frac{1}{x_1-x_2}\sum_{i=1}^{+\infty}(x_2^{-i}-x_1^{-i})x^i \end{aligned} \]

  拆得清清楚楚啦,答案:

\[[x^n]G(x)=(x_2-x_1)^{-1}(x_2^{-n}-x_1^{-n}) \]

  代入 \(x_{1,2}\)

\[[x^n]G(x)=-\frac{\sqrt2}4[(1-\sqrt2)^n-(1+\sqrt2)^n] \]

  最後 \(\sqrt2\equiv 59713600\equiv 940286407\pmod{10^9+7}\),所以可以 \(\mathcal O(\log p+\log n)\)\(p\) 是素模數)直接算出來。

\(\mathcal{Code}\)

/* Clearink */

#include <cstdio>

const int INV4 = 250000002, S2 = 59713600, MOD = 1e9 + 7;

inline int rmod () {
	int x = 0; char s = getchar ();
	for ( ; s < '0' || '9' < s; s = getchar () );
	for ( ; '0' <= s && s <= '9'; s = getchar () ) {
		x = ( x * 10ll + ( s ^ '0' ) ) % ( MOD - 1 );
	}
	return x;
}

inline int mul ( const long long a, const int b ) { return a * b % MOD; }
inline int sub ( int a, const int b ) { return ( a -= b ) < 0 ? a + MOD : a; }
inline int add ( int a, const int b ) { return ( a += b ) < MOD ? a : a - MOD; }
inline int mpow ( int a, int b ) {
	int ret = 1;
	for ( ; b; a = mul ( a, a ), b >>= 1 ) ret = mul ( ret, b & 1 ? a : 1 );
	return ret;
}

int main () {
	int n = rmod ();
	printf ( "%d\n", sub ( 0, mul ( mul ( S2, INV4 ),
		sub ( mpow ( sub ( 1, S2 ), n ), mpow ( add ( 1, S2 ), n ) ) ) ) );
	return 0;
}