1. 程式人生 > >【bzoj5118】Fib數列2 費馬小定理+矩陣乘法

【bzoj5118】Fib數列2 費馬小定理+矩陣乘法

n-2 fin 描述 print std fine ont str CP

題目描述

Fib定義為Fib(0)=0,Fib(1)=1,對於n≥2,Fib(n)=Fib(n-1)+Fib(n-2) 現給出N,求Fib(2^n).

輸入

本題有多組數據。第一行一個整數T,表示數據組數。 接下來T行每行一個整數N,含義如題目所示。 n≤10^15, T≤5

輸出

輸出共T行,每行一個整數為所求答案。 由於答案可能過大,請將答案mod 1125899839733759後輸出

樣例輸入

2
2
31

樣例輸出

3
343812777493853


題解

費馬小定理+矩陣乘法

傻逼題,根據費馬小定理,指數在模 $p-1$ 意義下相等時冪數相等。

因此求出 $2^n$ 在模 $p-1$ 意義下的結果,再用矩陣乘法維護fib數列,求矩陣的 $2^n\ \text{mod}\ (p-1)$ 次冪即可。

模數較大因此使用快(man)速乘,時間復雜度 $O(\log^2n)$ 。

#include <cstdio>
#include <cstring>
#define mod 1125899839733759
typedef long long ll;
inline ll mul(ll x , ll y , ll p)
{
	ll ans = 0;
	while(y)
	{
		if(y & 1) ans = (ans + x) % p;
		x = (x + x) % p , y >>= 1;
	}
	return ans;
}
inline ll pow(ll x , ll y , ll p)
{
	ll ans = 1;
	while(y)
	{
		if(y & 1) ans = mul(ans , x , p);
		x = mul(x , x , p) , y >>= 1;
	}
	return ans;
}
struct data
{
	ll v[2][2];
	data() {memset(v , 0 , sizeof(v));}
	ll *operator[](int a) {return v[a];}
	data operator*(data a)
	{
		data ans;
		int i , j , k;
		for(i = 0 ; i < 2 ; i ++ )
			for(k = 0 ; k < 2 ; k ++ )
				for(j = 0 ; j < 2 ; j ++ )
					ans[i][j] = (ans[i][j] + mul(v[i][k] , a[k][j] , mod)) % mod;
		return ans;
	}
	data operator^(ll y)
	{
		data x = *this , ans;
		ans[0][0] = ans[1][1] = 1;
		while(y)
		{
			if(y & 1) ans = ans * x;
			x = x * x , y >>= 1;
		}
		return ans;
	}
}A;
int main()
{
	int T;
	scanf("%d" , &T);
	while(T -- )
	{
		ll n;
		scanf("%lld" , &n) , n = pow(2 , n , mod - 1);
		A[0][0] = 0 , A[0][1] = A[1][0] = A[1][1] = 1 , A = A ^ n;
		printf("%lld\n" , A[1][0]);
	}
	return 0;
}

【bzoj5118】Fib數列2 費馬小定理+矩陣乘法