J - Sequence HDU - 6395(矩阵快速幂+类比思想(第一次把线性代数矩阵运算运用到这里(还是挺开心的)))

在这里插入图片描述
在这里插入图片描述
这道题不仅仅是简单的斐波那契数列矩阵快速幂了;还有分段思想(这里还用到了数论):
解决这道题,我觉得应该先把矩阵快速幂搞得清清楚楚才行,其实矩阵快速幂和一般我们写的指数的快速幂是一个道理(类比思想);
首先说最简单的矩阵快速幂吧:
我们可以利用结构体知识或者class知识都可以写;
这里给出结构体版本(解决最简单的斐波那契数列的计算):
在这里插入图片描述
那么:
对于计算(第n项的值)斐波那契数列就很easy了(这里是取了Mod的,不然会越界):

#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define Mod 100000007
typedef long long ll;
#define N 2//这里表示2*2的矩阵//如果是3*3的  则可以令为N 3 
struct Mu{
  ll	a[N][N];
  Mu(){
  	   memset(a,0,sizeof(a));
  }
};
Mu Multiply(Mu t1,Mu t2){//求两个矩阵相乘 
	  Mu res;
	  for(ll i=0;i<N;i++){
	  	  for(ll j=0;j<N;j++){
	  	  	   for(ll k=0;k<N;k++)
	  	  	   {
	  	  	   	      res.a[i][j]=(res.a[i][j]+t1.a[i][k]*t2.a[k][j])%Mod;
				   }
			}
	  }
	  return res;
} 
Mu QP(Mu x,ll n)//矩阵快速幂 返回结果对象 
{
	   Mu res;
	   res.a[0][0]=res.a[1][1]=res.a[2][2]=1;//单位矩阵
	   while(n){
	   	  if(n&1){
	   	  	   res=Multiply(res,x);
			 }
			 x=Multiply(x,x);
			 n>>=1;
	   } 
	   return res;
}
Mu ans;
ll n,F1=1,F2=1;
void init(){
	//初始化矩阵
  //比如斐波那契数列 
    ans.a[0][0]=ans.a[0][1]=ans.a[1][0]=1;
}
void Ans(){
	 ans=QP(ans,n-2);//算ans^n
	cout<<(ans.a[0][0]*F1+ans.a[0][1]*F2)%Mod<<endl;
}
int main(){
	cin>>n;
	if(n==1) return cout<<F1<<endl,0;
	else if(n==2) return  cout<<F2<<endl,0;
	init();
	Ans();
	return 0;
}

在这里插入图片描述
是不是很强大,嘻嘻;但是解决这道题还远远不够;
这道题第一点就是递推公式不好找,二点就是分段问题是个难点(因为平常分会超时),解决了这两点就可以AC这道题了;
直接看代码吧(主要是对自定义类传递参数和Java里面不同,我现在能够分的很清楚了!!嘻嘻);
递推公式:
在这里插入图片描述
在这里插入图片描述
所以由于矩阵乘法结合律所以这里从往左算;

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define Mod 1000000007
struct Mu{
	ll a[3][3];//就是这里我WA了很多次,没有写ll结果自己Debug了半天  我还以为自己把快速幂写错了,太无语了
	Mu(){//构造函数
		  memset(a,0,sizeof(a));
	}
};
Mu mu(Mu t1,Mu t2){
	  Mu res;
	  for(ll i=0;i<3;i++){
	  	  for(ll j=0;j<3;j++){
	  	  	   for(ll k=0;k<3;k++){
	  	  	   	   res.a[i][j]=(res.a[i][j]+t1.a[i][k]*t2.a[k][j])%Mod;
				   }
			}
	  }
	  return res;
}
Mu QP(Mu x,int n){
	Mu res;
	res.a[0][0]=res.a[1][1]=res.a[2][2]=1;
	while(n){
		  if(n&1){
		  	   res=mu(res,x);
		  }
		  x=mu(x,x);
		  n>>=1;
	}
	return res;
}
//int Period(int P,int i){
//	  int j;
//
//	  for(j=i;(P/i)==(P/j);j++);
//	  return j;
//}
int main(){
	ll T,A,B,C,D,P,n;
	scanf("%lld",&T);
	while(T--){
		  scanf("%lld %lld %lld %lld %lld %lld",&A,&B,&C,&D,&P,&n);
		   if(n==1) {printf("%lld\n",A);continue;}
		  if(n==2) {printf("%lld\n",B);continue;}
		  ll j;
		  	Mu s,ans;
		  for(int i=3;i<=n;i=j+1){
		  	//memset(s.a,0,sizeof(s.a));//因为每次都需要置为0
		  	s.a[0][0]=D;s.a[0][1]=C;s.a[0][2]=P/i;
		  	s.a[1][0]=1;
		  	s.a[2][2]=1;
		  	//算阶段幂
	       if(i>P){//这里是算最后一次
	       	    ans=QP(s,n-i+1);
	       	    B=(ans.a[0][0]*B%Mod+ans.a[0][1]*A%Mod+ans.a[0][2])%Mod;
				 break;    
		   }
		     j=min(n,P/(P/i));//这里必须要比最小值,用如果大于了n就不成立了
		     ans=QP(s,j-i+1);
		   ll t1=(ans.a[0][0]*B+ans.a[0][1]*A+ans.a[0][2])%Mod;//这里就是为什么需要更新A,B,因为由上面图的分析可以知道
		   ll t2=(ans.a[1][0]*B+ans.a[1][1]*A+ans.a[1][2])%Mod;
		   B=t1;A=t2;//因为这里的矩阵是倒起来算的,所以需要每次更新A,B
		  }
		  printf("%lld\n",B);//最后输出B
		  
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_44555205/article/details/97970000