HDU6395 Sequence 【矩阵快速幂】【分块】

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6395

题意:
F ( n ) = D ∗ F ( n − 1 ) + C ∗ F ( n − 2 ) + [ P / n ] F(n)=D*F(n-1)+C*F(n-2)+[P/n] F(n)=DF(n1)+CF(n2)+[P/n]的第n项,其中 F ( 1 ) = a , F ( 2 ) = b F(1)=a,F(2)=b F(1)=a,F(2)=b

题解:
注意到因为递推式的常数项是个变量,所以不能无脑矩阵快速幂
这个函数很眼熟…分块?
我们对 P/i 分块,可以构造单位矩阵:
D   C   P / i D \ C \ P/i D C P/i
1   0   0 1 \ 0 \ 0 1 0 0
1   0   0 1 \ 0 \ 0 1 0 0
然后对于此时 P/i 就是固定的了,跑矩阵快速幂即可
此题卡常,matrix 里以前 ma[3][3]是ma[15][15]的就会T

// by Balloons
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#define debug() puts("okkkkkkkk")
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)

using namespace std;

typedef long long LL;
#define int LL

const int inf = 1 << 30,mod=1e9+7;
int a,b,c,d,p,n;

struct matrix{
	int ma[3][3];
	matrix(){memset(ma,0,sizeof ma);}
};

matrix operator *(matrix a,matrix b){
	matrix tmp;
	for(int i=0;i<3;i++)
		for(int j=0;j<3;j++)
			for(int k=0;k<3;k++)
				(tmp.ma[i][j]+=a.ma[i][k]*b.ma[k][j])%=mod;
	return tmp;
}

matrix pow(matrix a,LL b){
	matrix tmp;
	for(int i=0;i<3;i++)tmp.ma[i][i]=1;
	
	while(b){
		if(b&1ll)tmp=tmp*a;
		b>>=1ll;
		a=a*a;
	}
	return tmp;
}

void work(){
	scanf("%lld%lld%lld%lld%lld%lld",&a,&b,&c,&d,&p,&n);
	if(n==1){
		printf("%lld\n",a);
		return ;
	}
	if(n==2){
		printf("%lld\n",b);
		return ;
	}
	int ok=0;
	for(LL i=3;i<=n;){
		if(p/i==0){
			matrix aa;
			aa.ma[0][0]=d;aa.ma[0][1]=c;aa.ma[0][2]=p/i;
			aa.ma[1][0]=1;aa.ma[2][2]=1;
			aa=pow(aa,n-i+1); 
			int res=aa.ma[0][0]*b+aa.ma[0][1]*a+aa.ma[0][2];
			res%=mod;
			printf("%lld\n",res);
			ok=1;
			break;
		}
		int j=min(n,p/(p/i));
		matrix aa;
		aa.ma[0][0]=d;aa.ma[0][1]=c;aa.ma[0][2]=p/i;
		aa.ma[1][0]=1;aa.ma[2][2]=1;
		
		matrix tmp=pow(aa,j-i+1);
		int A=tmp.ma[1][0]*b+tmp.ma[1][1]*a+tmp.ma[1][2];
		int B=tmp.ma[0][0]*b+tmp.ma[0][1]*a+tmp.ma[0][2];
		A%=mod;B%=mod;
		
		a=A;b=B;
		i=j+1;
	}
	if(!ok)printf("%lld\n",b);
}

signed main(){
	int T;scanf("%d",&T);
	while(T--)work();

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值