hdu5451 Best Solver (广义斐波那契数列求通项+矩阵快速幂)

写在最前面,方便复习:
已知数列 { a n } \{a_n\} {an} 其满足递推式 a n + 2 = c 1 a n + 1 + c 2 a n a_{n+2} = c_1a_{n+1}+c_2a_n an+2=c1an+1+c2an 其中 c 1 , c 2 c_1,c_2 c1,c2 为常数且 c 2 ≠ 0 c_2 \not =0 c2=0
x 2 = c 1 x + c 2 x^2=c_1x+c_2 x2=c1x+c2 的两根为 x 1 , x 2 x_1,x_2 x1,x2 ,此时有:

  1. x 1 ≠ x 2 x1\not= x_2 x1=x2 时, a n = b 1 x 1 n + b 2 x 2 n a_n=b_1x_1^n+b_2x_2^n an=b1x1n+b2x2n b 1 , b 2 b_1,b_2 b1,b2 满足 { a 1 = b 1 x 1 + b 2 x 2 a 2 = b 1 x 1 2 + b 2 x 2 2 \left \{ \begin{array}{c} a_1=b_1x_1+b_2x_2\\ a_2=b_1x_1^2+b_2x_2^2\\ \end{array} \right. {a1=b1x1+b2x2a2=b1x12+b2x22
  2. x 1 = x 2 = x x1= x_2=x x1=x2=x 时, a n = b 1 x 1 n + b 2 x 2 n a_n=b_1x_1^n+b_2x_2^n an=b1x1n+b2x2n b 1 , b 2 b_1,b_2 b1,b2 满足 { a 1 = ( b 1 + b 2 ) x a 2 = ( b 1 + b 2 ) x 2 \left \{ \begin{array}{c} a_1=(b_1+b_2)x\\ a_2=(b_1+b_2)x^2\\ \end{array} \right. {a1=(b1+b2)xa2=(b1+b2)x2

题目链接:点击这里

题目大意:
t t t 组输入,每组输入给定正整数 x ( 1 ≤ x < 2 32 ) x(1\le x < 2^{32}) x(1x<232) ,和质数 p ( p ≤ 46337 ) p(p\le 46337) p(p46337) 求:
⌊ y = ( 5 + 2 6 ) 1 + 2 x ⌋ m o d    p \lfloor y=(5+2\sqrt6)^{1+2x}\rfloor \mod p y=(5+26 )1+2xmodp

题目分析:
x 1 = 5 + 2 6 , x 2 = 5 − 2 6 x_1=5+2\sqrt6,x_2=5-2\sqrt6 x1=5+26 ,x2=526 ,则这两根显然是方程 x 2 − 10 x + 1 = 0 x^2-10x+1=0 x210x+1=0 的两根
故其递推方程为 a n + 2 = 10 a n + 1 − a n a_{n+2}=10a_{n+1}-a_n an+2=10an+1an ,其通项为 a n = ( 5 + 2 6 ) n + ( 5 − 2 6 ) n a_n=(5+2\sqrt6)^n+(5-2\sqrt6)^n an=(5+26 )n+(526 )n
因为 5 − 2 6 < 1 5-2\sqrt6 < 1 526 <1 ,所以 ( 5 − 2 6 ) n < 1 (5-2\sqrt6)^n<1 (526 )n<1 ,所以题目所求就是 a n − 1 a_n-1 an1
因为此题模数不到 1 e 5 1e5 1e5 所以循环节不会很长可以直接暴力算出,但是如果模数很大则需要构造矩阵来求循环节,具体方法点击这里
之所以要找循环节是因为 2 x 2^x 2x 很大 对于每一个模数都存在循环节,这样就可以避免直接进行幂次运算

具体细节见代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<set>
#include<map>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
ll read()
{
	ll res = 0,flag = 1;
	char ch = getchar();
	while(ch<'0' || ch>'9')
	{
		if(ch == '-') flag = -1;
		ch = getchar();
	}
	while(ch>='0' && ch<='9')
	{
		res = (res<<3)+(res<<1)+(ch^48);//res*10+ch-'0';
		ch = getchar();
	}
	return res*flag;
}
const int maxn = 1e5+5;
ll mod;
const double pi = acos(-1);
const double eps = 1e-8;
ll n,cnt,a[maxn],loop[maxn];
ll get_loop()
{
	a[0] = 2%mod,a[1] = 10%mod;
	for(int i = 2;i < maxn;i++)
	{
		a[i] = (10*a[i-1]%mod-a[i-2]+mod)%mod;
		if(a[i] == a[1] && a[i-1] == a[0])
			return i-1;
	}
}
ll qpow(ll a,ll b,ll mod)
{	
	ll res = 1;
	while(b)
	{
		if(b&1) res = res*a%mod;
		a = a*a%mod;
		b >>= 1;
	}
	return res;
}
int main()
{
	ll t = read();
	while(t--)
	{
		n = read(),mod = read();
		if(!loop[mod]) loop[mod] = get_loop(); //这里要记忆化一下不然就t掉了
		ll pos = (1+qpow(2,n,loop[mod]))%loop[mod]; 
		for(int i = 2;i <= pos;i++) //因为记忆化过所以上次get_loop 不一定枚举到了pos,所以需要重新扫一遍
			a[i] = (10*a[i-1]%mod-a[i-2]+mod)%mod;
		printf("Case #%lld: %lld\n",++cnt,(a[pos]-1+mod)%mod);
	}
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值