51nod 1355斐波那契数列的最小公倍数

woc好神

不愧是8级题

题意:

给定 \(n(n\le 50000)\) 个正整数 \(a_i(1\le a_i\le 10^6)\)

你需要计算:

\[\text{lcm}(F(a_1),F(a_2)...F(a_n)) \]

答案对\(1e9+7\)取模。

其中\(F(i)\)表示斐波那契数列第 \(i\) 项。

\(\rm Sol:\)

我们应当注意到一个这样的基本事实:

考虑到\(\rm lcm\)可以看作对于因数分解下的每一位取\(\max\),而\(\gcd\)则可以看作对因数分解下的每一位取\(\min\)

于是我们会发现在我们一无所知的情况下我们所求的\(\rm lcm\)\(\gcd\)居然可以通过\(\min -\max\)容斥处理。

具体来说是因为\(\rm lcm\)看作指数取\(\max\)\(\rm gcd\)看作指数取\(\min\)所以我们会发现其相当于对于指数做一个\(\min-\max\)容斥即可。

然后我们发现每个因数的贡献是乘起来的,所以我们得到:

\[\text{lcm}(S)=\prod_{T\sub S}\gcd(T)^{(-1)^{|T|+1}} \]

这个式子丢到指数上一看就能明白正确性了(

然后又因为对于斐波那契数列而言,存在:\(\gcd(f(a),f(b))=f(\gcd(a,b))\)

于是我们所求为:

\[\prod_{T\sub S}f(\gcd(a...))^{(-1)^{|T|+1}} \]

注意到值域并不大,我们考虑莫比乌斯反演,考虑化简原式应当为:

\[\prod_{i=1}^{10^6} f(i)^{[\gcd_{i\in T}(a_i)==i](-1)^{(|T|+1)}} \]

我们考虑统计上式,不妨令\(F(x)\)表示\(\gcd\) 至少是\(x\)的指数贡献和

初步考虑后注意到对于\(F(x)\),假定有\(k\)\(i\)满足\(x|a_i\),则可知\(F(x)\)为:

\[\sum_{i=1}^{k}\dbinom{k}{i}(-1)^{|i|+1} \]

注意到这个式子的值恒定为:\([k\ne 0]\)

然后我们令\(g(i)\)表示恰好

则可得:

\[F(x)=\sum_{x|d}g(d) \]

\[g(d)=\sum_{d|x}\mu(\frac{x}{d})F(x) \]

直接反演即可。

注意到答案肯定很小,我们甚至不需要使用欧拉定理。

然后这道题的斐波那契数列实际上是当作\(f_1=1,f_2=1\)这样推的= =,这一点破坏了这道题的和谐美感。

\(Code:\)

#include<bits/stdc++.h>
using namespace std ;
#define rep( i, s, t ) for( register int i = s; i <= t; ++ i )
#define re register
#define int long long
int gi() {
	char cc = getchar() ; int cn = 0, flus = 1 ;
	while( cc < '0' || cc > '9' ) {  if( cc == '-' ) flus = - flus ; cc = getchar() ; }
	while( cc >= '0' && cc <= '9' )  cn = cn * 10 + cc - '0', cc = getchar() ;
	return cn * flus ;
}
const int M = 1e6 + 5 ;
const int N = 1e5 + 5 ;
const int P = 1e9 + 7 ;
int n, m, a[N], mu[M], p[N], f[M], F[M], Ans, top ; 
bool isp[M], cnt[M] ; 
void init( int x ) {
	f[2] = f[1] = 1, mu[1] = 1, isp[1] = 1 ;
	for( re int i = 2; i <= x; ++ i ) {
		if( i != 2 ) f[i] = ( f[i - 1] + f[i - 2] ) % P ;
		if( !isp[i] ) p[++ top] = i, mu[i] = -1 ;
		for( re int j = 1; j <= top && p[j] * i <= x; ++ j ) {
			isp[p[j] * i] = 1 ;
			if( i % p[j] == 0 ) break ;
			mu[i * p[j]] = - mu[i] ;
		}
	}
}
int fpow( int x, int k ) {
	int ans = 1, base = x ; 
	while( k ) {
		if( k & 1 ) ans = ans * base % P ;
		base = base * base % P, k >>= 1 ; 
	} return ans % P ; 
}
signed main()
{
	n = gi(), init(1e6), Ans = 1 ; 
	rep( i, 1, n ) a[i] = gi(), cnt[a[i]] = 1, m = max( m, a[i] ) ;
	for( re int i = 1; i <= m; ++ i )
	for( re int j = i; j <= m; j += i ) cnt[i] |= cnt[j] ;
	for( re int i = 1; i <= m; ++ i )
	for( re int j = i; j <= m; j += i ) F[i] += mu[j / i] * cnt[j] ;
	for( re int i = 1; i <= m; ++ i ) {
		if( F[i] > 0 ) Ans = ( Ans * fpow( f[i], F[i] ) % P ) ;
		if( F[i] < 0 ) Ans = ( Ans * fpow( fpow( f[i], -F[i] ), P - 2 ) ) % P ;
	}
	printf("%lld\n", Ans ) ;
	return 0 ;
}
posted @ 2020-02-02 11:32  Soulist  阅读(182)  评论(0编辑  收藏  举报