2018.09.27【BZOJ5221】偏题(矩阵快速幂)(数学推理)

【问题描述】

斐波那契数列是一个经典递推数列,即 F i b n = F i b n − 1 + F i b n − 2 Fib_n=Fib_{n-1}+Fib_{n-2} Fibn=Fibn1+Fibn2
在这个问题中,定义一个新数列,对于 n ≥ 2 n ≥ 2 n2 F n = F n − 1 + F n − 2 + 3 + F n − 1 F n − 2 F_n=F_{n-1}+F_{n-2}+\sqrt{3+F_{n-1}F_{n-2}} Fn=Fn1+Fn2+3+Fn1Fn2
对于给出的四个整数 F 0 , F 1 , M , n F_0,F_1,M,n F0,F1,M,n,求 F n   m o d   M F_n\text{ }mod\text{ }M Fn mod M
数据保证 3 + F n − 1 F n − 2 \sqrt{3+F_{n-1}F_{n-2}} 3+Fn1Fn2 总是整数。

【输入格式】

从文件 fi b . i n fib.in b.in中读入数据。
一行四个整数 F 0 , F 1 , M , n F_0,F_1,M,n F0,F1,M,n

【输出格式】

输出到文件 fi b . o u t fib.out b.out中。
一行一个整数,表示 F n F_n Fn M M M取模后的值。

【样例输入1】

1 1 10 5

【样例输出1】

4

【样例解释1】

F 0 = 1 F_0 = 1 F0=1
F 1 = 1 F_1 = 1 F1=1
F 2 = 1 + 1 + 2 3 + 1 × 1 = 6 F_2 = 1 + 1 + 2 \sqrt {3 + 1 × 1} = 6 F2=1+1+23+1×1 =6
F 3 = 6 + 1 + 2 3 + 6 × 1 = 13 F_3 = 6 + 1 + 2\sqrt{3 + 6 × 1} = 13 F3=6+1+23+6×1 =13
F 4 = 13 + 6 + 2 3 + 13 × 6 = 37 F_4 = 13 + 6 + 2\sqrt{3 + 13 × 6 }= 37 F4=13+6+23+13×6 =37
F 5 = 37 + 13 + 2 3 + 37 × 13 = 94 F_5 = 37 + 13 + 2\sqrt{3 + 37 × 13} = 94 F5=37+13+23+37×13 =94

【样例输入2】

2 3 100 6

【样例输出2】

82

【数据规模】

对于30%的数据, n n n ≤ 20。
对于60%的数据, F 0 = F 1 = 1 F_0 = F_1 = 1 F0=F1=1
对于80%的数据, n ≤ 1 0 5 n ≤ 10^5 n105
对于100%的数据, 0 ≤ n ≤ 1 0 9 0 ≤ n ≤ 10^9 0n109 1 ≤ M ≤ 1 0 9 1 ≤ M ≤ 10^9 1M109 1 ≤ F 0 ≤ F 1 ≤ 1 0 6 1 ≤ F_0 ≤ F_1 ≤ 10^6 1F0F1106


解析:

看到以为不是线性递推,整个人就方了。

然而听到有dalao说是二次剩余,貌似并没有 A A A掉此题。

思路:

由上面的递推式我们有结论 3 + F n F n − 1 = 3 + F n − 1 F n − 2 + F n − 1 \sqrt{3+F_nF_{n-1}}=\sqrt{3+F_{n-1}F_{n-2}}+F_{n-1} 3+FnFn1 =3+Fn1Fn2 +Fn1

直接构造矩阵,快速幂推一下就行了。


代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const

inline
ll getint(){
	re ll num;
	re char c;
	while(!isdigit(c=gc()));num=c^48;
	while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
	return num;
}

ll mod;
ll n,F1,F0;

struct matrix{
	ll a[3][3];
	matrix(){memset(a,0,sizeof a);}
	
	void origin(){
		a[0][0]=1,a[0][1]=0,a[0][2]=0;
		a[1][0]=0,a[1][1]=1,a[1][2]=0;
		a[2][0]=0,a[2][1]=0,a[2][2]=1;
	}
	
	void init(){
		a[0][0]=1,a[0][1]=1,a[0][2]=1;
		a[1][0]=1,a[1][1]=0,a[1][2]=0;
		a[2][0]=2,a[2][1]=0,a[2][2]=1;
	}
	
	ll *const operator[](cs int &offset){
		return a[offset];
	}
	
	friend matrix operator*(matrix t,matrix b){
		matrix c;
		(c[0][0]=t[0][0]*b[0][0]%mod+t[0][1]*b[1][0]%mod+t[0][2]*b[2][0]%mod)%=mod;
		(c[0][1]=t[0][0]*b[0][1]%mod+t[0][1]*b[1][1]%mod+t[0][2]*b[2][1]%mod)%=mod;
		(c[0][2]=t[0][0]*b[0][2]%mod+t[0][1]*b[1][2]%mod+t[0][2]*b[2][2]%mod)%=mod;
		(c[1][0]=t[1][0]*b[0][0]%mod+t[1][1]*b[1][0]%mod+t[1][2]*b[2][0]%mod)%=mod;
		(c[1][1]=t[1][0]*b[0][1]%mod+t[1][1]*b[1][1]%mod+t[1][2]*b[2][1]%mod)%=mod;
		(c[1][2]=t[1][0]*b[0][2]%mod+t[1][1]*b[1][2]%mod+t[1][2]*b[2][2]%mod)%=mod;
		(c[2][0]=t[2][0]*b[0][0]%mod+t[2][1]*b[1][0]%mod+t[2][2]*b[2][0]%mod)%=mod;
		(c[2][1]=t[2][0]*b[0][1]%mod+t[2][1]*b[1][1]%mod+t[2][2]*b[2][1]%mod)%=mod;
		(c[2][2]=t[2][0]*b[0][2]%mod+t[2][1]*b[1][2]%mod+t[2][2]*b[2][2]%mod)%=mod;
		return c;
	}
}ss;

inline
matrix quickpow(ll b){
	matrix a;
	a.init();
	matrix ans;
	ans.origin();
	while(b){
		if(b&1){
			ans=ans*a;
		}
		a=a*a;
		b>>=1;
	}
	return ans;
}

signed main(){
	F0=getint();
	F1=getint();
	mod=getint();
	n=getint();
	if(n==0){
		cout<<F0%mod;
		return 0;
	}
	ss[0][0]=F1%mod;
	ss[0][1]=F0%mod;
	ss[0][2]=sqrt(3ll+F1*F0);
	ss[0][2]%=mod;
	ss=ss*quickpow(n-1);
	cout<<ss[0][0]<<endl;
	pc('\n');
	/*循环展开打表代码
	freopen("task.txt","w",stdout);
	for(int re i=0;i<3;++i)
	for(int re j=0;j<3;++j)
	cout<<"(c["<<i<<"]["<<j<<"]=a["<<i<<"][0]*b[0]["<<j<<"]%mod+a["<<i<<"][1]*b[1]["<<j<<"]%mod+a["<<i<<"][2]*b[2]["<<j<<"]%mod)%=mod;"<<endl; 
	*/
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值