「BZOJ4833」最小公倍佩尔数

题目

对于非负整数 \(n\),令 \((1+\sqrt 2)^n=e_n+f_n\sqrt 2\)

\(g_n=\operatorname{lcm}_{1\le k\le n}f_k\)。给定正整数 \(n\) 和质数 \(p\),求 \(\sum_{k=1}^n kg_k\bmod p\)

多组测试,所有测试点满足 \(1\le n\le 10^6,1\le \sum n\le 3\times 10^6\),且 \(\forall 1\le k\le n,p\nmid f_k\)

分析

容易得到递推关系:

\[\begin{aligned} \begin{pmatrix} e_0\\ f_0 \end{pmatrix} &= \begin{bmatrix} 1\\ 0 \end{bmatrix}\\ \begin{pmatrix} e_n\\ f_n \end{pmatrix} &= \begin{bmatrix} 1&2\\ 1&1 \end{bmatrix} \begin{pmatrix} e_{n-1}\\ f_{n-1} \end{pmatrix},\forall n\ge 1 \end{aligned} \]

注意到答案最后和 \(e\) 没关系,所以我们尝试消掉 \(e\),得到只关于 \(f\) 的式子:

\[\begin{aligned} f_0&=0\\ f_{n}&=1+f_{n-1}+2\sum_{k=0}^{n-2}f_{k},\forall n\ge 1 \end{aligned} \]

进而得到一个递推关系:

\[\begin{aligned} f_0&=0,f_1=1\\ f_n&=2f_{n-1}+f_{n-2},\forall n\ge 2 \end{aligned} \]

注意到这个 \(f\) 有两个很好的性质:

  1. \(\gcd(f_n,f_{n-1})=1\),考察 \(\gcd\) 时必看。

  2. \(f_{n+m}=f_{m+1}f_n+f_{m}f_{n-1}\),考察转移矩阵可以得到这一点。

由这两点,将其和 Fibonacci 数列进行类比,我们可以得到:

\[\gcd(f_{n},f_{m})=f_{\gcd(n,m)},\forall n,m\in \mathbb Z_+ \]

Proof.

不妨设 \(m=n+k,k\in \mathbb N\)

则:

\[\begin{aligned} \gcd(f_n,f_m) &=\gcd(f_n,f_{n+k})\\ &=\gcd(f_n,f_{k+1}f_n+f_{k}f_{n-1})\\ &=\gcd(f_n,f_{n-1}f_k)\\ &=\gcd(f_n,f_k)=\gcd(f_n,f_{m-n}) \end{aligned} \]

进而可以得到 \(\gcd(f_n,f_m)=\gcd(f_n,f_{m-\lfloor\frac m n\rfloor n})=\gcd(f_n,f_{m\bmod n})\)。反复利用此关系即可证明。

最后,\(g_n\) 可以直接根据 min-max 反演通过 \(f\)\(\gcd\) 计算得到:

\[g_n=\prod_{d=1}^nf_d^{\sum_{k=1}^{\lfloor\frac n d\rfloor}\mu(k)} \]

\(g_n\) 可以在 \(O(n\log n)\) 的时间内处理出来,于是问题就解决了。

代码

#include <cstdio>
#include <iostream>

#define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
#define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )

typedef long long LL;

const int MAXN = 1e6 + 5;

template<typename _T>
inline void Read( _T &x ) {
	x = 0; char s = getchar(); bool f = false;
	while( ! ( '0' <= s && s <= '9' ) ) { f = s == '-', s = getchar(); }
	while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar(); }
	if( f ) x = -x;
}

template<typename _T>
inline void Write( _T x ) {
	if( x < 0 ) putchar( '-' ), x = -x;
	if( 9 < x ) Write( x / 10 );
	putchar( x % 10 + '0' );
}

int tag[MAXN];

int f[MAXN], mu[MAXN];

int N, mod;

inline int Qkpow( int, LL );
inline int Inv( const int &a ) { return Qkpow( a, mod - 2 ); }
inline int Mul( int x, const int &v ) { return 1ll * x * v % mod; }
inline int Sub( int x, const int &v ) { return ( x -= v ) < 0 ? x + mod : x; }
inline int Add( int x, const int &v ) { return ( x += v ) >= mod ? x - mod : x; }

inline int& MulEq( int &x, const int &v ) { return x = 1ll * x * v % mod; }
inline int& SubEq( int &x, const int &v ) { return ( x -= v ) < 0 ? ( x += mod ) : x; }
inline int& AddEq( int &x, const int &v ) { return ( x += v ) >= mod ? ( x -= mod ) : x; }

inline int Qkpow( int base, LL indx ) {
	int ret = 1;
	while( indx ) {
		if( indx & 1 ) MulEq( ret, base );
		MulEq( base, base ), indx >>= 1;
	}
	return ret;
}

inline void Init( const int &n ) {
	mu[1] = 1;
	for( int i = 1 ; ( i << 1 ) <= n ; i ++ )
		for( int j = i << 1 ; j <= n ; j += i )
			mu[j] -= mu[i];
}

int main() {
	Init( 1e6 );
	int T; Read( T );
	while( T -- ) {
		Read( N ), Read( mod );

		f[0] = 0, f[1] = 1;
		rep( i, 1, N ) tag[i] = 1;
		rep( i, 2, N ) f[i] = Add( Mul( 2, f[i - 1] ), f[i - 2] );
		for( int d = 1 ; d <= N ; d ++ ) {
			int fInv = Inv( f[d] );
			for( int j = 1 ; d * j <= N ; j ++ ) 
				if( mu[j] == 1 )
					MulEq( tag[j * d], f[d] );
				else if( mu[j] == -1 )
					MulEq( tag[j * d], fInv );
		}
		int ans = 0;
		rep( i, 2, N ) MulEq( tag[i], tag[i - 1] );
		rep( i, 1, N ) AddEq( ans, Mul( i, tag[i] ) );
		Write( ans ), putchar( '\n' );
	}
	return 0;
}
posted @ 2023-03-20 17:16  crashed  阅读(59)  评论(0编辑  收藏  举报