M斐波那契数列 HDU 4549(矩阵快速幂+费马小定理)

这题一开始···我找出构造矩阵了,但是由于对费马小定理不太熟悉,所以并没有做出来,学习后,记录一下。

题目给的

F[0]=a

F[1]=b

F[n]=F[n-1]*F[n-2]

这道题是没有办法直接写出对应F[n]的矩阵的,原因是因为无法分解因式。

但是有一种乘法中是蕴含加法的,就是同底相乘,指数相加。观察如下:

a,b,ab,ab^{2},a^{2}b^{3},a^{3}b^{5},a^{5}b^{8}

很容易发现a与b 的指数就是一个斐波那契数列,那么我们很容易通过下面这个矩阵求出指数

\begin{bmatrix} f[n] \\f[n-1] \end{bmatrix} = \begin{bmatrix} 1 &1 \\ 1&1 \end{bmatrix} \begin{bmatrix} f[n-1] \\f[n-2] \end{bmatrix}

但是求出指数之后,你还是会WA的。

因为a^{x}b^{y}!=a^{x mod p}b^{y ^mod p} (mod p)

所以指数也是会炸ll的,那怎么解决呢?这时候就要用到费马小定理了

费马小定理:

当gcd(a,p)=1时,有

a^{p-1} \equiv 1 (mod p)

所以就有

\large a^{x}b^{y}\equiv a^{x mod (p-1)}b^{ymod(p-1)}(mod p)

于是问题就完美解决了

AC代码,尚可优化

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define up(i,a,n) for(int i=a;i<=n;i++)
#define down(i,a,n) for(int i=a;i>=n;i--)
const int maxn=2;
const int md=1e9+7;
const int md1=1e9+6;
ll n,x,y;
struct node
{
	ll a[maxn][maxn];
	friend node operator * (node a,node b)
	{
	node c;
	memset(c.a,0,sizeof(c.a));
	up(i,0,1)
	up(j,0,1)
	up(k,0,1)
	c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j]%md1)%md1;
	return c;
    }
}unit;
void init()
{
   unit.a[0][0]=1;
   unit.a[0][1]=1;
   unit.a[1][0]=1;
   unit.a[1][1]=0; 
}
node pow_node(ll k)
{  
	node ans,a;
	a=unit;
	memset(ans.a,0,sizeof(ans.a));
	up(i,0,1)
	ans.a[i][i]=1;
	while(k)
	{
		if(k&1)ans=ans*a;
		k/=2;
		a=a*a;
	}
	return ans;
}
ll pow_num(ll a,ll k)
{
	ll ans=1;
	a%=md;
	while(k)
	{
		if(k&1)ans=((ans%md)*(a%md))%md;
		k/=2;
		a=((a%md)*(a%md))%md;
	}
	return ans;
}
int main()
{   
	while(~scanf("%lld %lld %lld",&x,&y,&n))
	{   init();
	    x%=md,y%=md;
	    if(n==0)
	    {
		printf("%lld\n",x%md);
		continue;
	    }
	    if(n==1)
        {
    	printf("%lld\n",y%md);
    	continue;
	    }
		if(n==2)
		{
			printf("%lld\n",x*y%md);
			continue;
		}
		if(n==3)
		{
			printf("%lld\n",x*y*y%md);
			continue;
		}
		node s=pow_node(n-3);
		ll ansa=0,ansb=0;
		ansa=(ansa+s.a[0][0]*1%md1)%md1;
		ansa=(ansa+s.a[0][1]*1%md1)%md1;
		ansb=(ansb+s.a[0][0]*2%md1)%md1;
		ansb=(ansb+s.a[0][1]*1%md1)%md1;
		ll tansa,tansb;
		tansa=pow_num(x,ansa);
		tansb=pow_num(y,ansb);
		printf("ansa=%lld ansb=%lld\n",ansa,ansb);
		ll ttans=(tansa*tansb)%md;
		printf("%lld\n",ttans);
	}
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值