[BZOJ4002] [JLOI2015] 有意义的字符串 [线性递推][斐波那契数列][特征方程]

[ L i n k \frak{Link} ]


一阶常系数线性递推式的特征方程

a n + 1 = k a n + d a_{n+1}=ka_n+d
k = 1 k=1 显然是等差数列,我们考虑 k 1 k\ne1
参数法:设 a n + 1 + t = k ( a n + t ) a_{n+1}+t=k(a_n+t)
a n + 1 = k a n + ( k 1 ) t a_{n+1}=ka_n+(k-1)t
那么 ( k 1 ) t = d (k-1)t=d
a n + 1 + d k 1 = k ( a n + d k 1 ) a_{n+1}+\frac{d}{k-1}=k(a_n+\frac{d}{k-1})
b n = a n + d k 1 b_n=a_{n}+\frac{d}{k-1}
容易解出等比数列 b n b_n 的通项
就可以推出 a n a_n 的通项


二阶常系数线性递推式的特征方程

仍然考虑类比参数法 可以通过一大堆复杂的推导变换得到。。。。
至于一般的常系数线性递推式也可以用生成函数分类讨论搞搞

结论:对 f ( n ) = c 1 f ( n 1 ) + c 2 f ( n 2 ) f(n)=c_1f(n-1)+c_2f(n-2)
移项 f ( n ) c 1 f ( n 1 ) c 2 f ( n 2 ) = 0 f(n)-c_1f(n-1)-c_2f(n-2)=0
导出 x 2 c 1 x c 2 = 0 x^2-c_1x-c_2=0
Δ 0 \Delta\ne0 f ( n ) = A x 1 n + B x 2 n f(n)=Ax_1^n+Bx_2^n
Δ = 0 \Delta=0 f ( n ) = ( A + B n ) x n f(n)=(A+Bn)x^n
利用已知的两个 f f 可以解出 A A B B 得到通项


特征根法

因为某考的原因 搞出了这样的结论来背:
仅对于 A a n + 1 a n + B a n + 1 + C a n + D = 0 Aa_{n+1}a_n+Ba_{n+1}+Ca_n+D=0 其中 a 0 a\ne0
导出 A x 2 + ( B + C ) x + D = 0 Ax^2+(B+C)x+D=0 其根称为特征根
Δ 0 \Delta\ne0 b n = a n x 1 a n x 2 b_n=\frac{a_n-x_1}{a_n-x_2}
Δ = 0 \Delta=0 b n = 1 a n x b_n=\frac{1}{a_n-x}
可以说是很牛逼了;怎么用呢?
先解特征根,设出 b n b_n
b n + 1 b_{n+1} b n b_n 的关系
然后就可以求 a n a_n 通项


本题

模数太丑了,设它为 p p
我们现在是要求 ( b + d 2 ) n ( m o d p ) \left\lfloor\left(\frac{b+\sqrt{d}}{2}\right)^n\right\rfloor\pmod p
发现 b , d , n b,d,n 都很大,考虑展开? i = 0 n ( n i ) b i ( d ) n i 2 n \left\lfloor\frac{\sum\limits_{i=0}^n{{n\choose i}b^i\left(\sqrt{d}\right)^{n-i}}}{2^n}\right\rfloor 还是搞不了
这个下取整也并没有留给我们什么好的性质,只能考虑究竟能不能快速计算那个幂
我们发现这东西好像半个斐波那契
那我们考虑 f ( n ) = ( b + d 2 ) n + ( b d 2 ) n f(n)=\left(\frac{b+\sqrt{d}}{2}\right)^n+\left(\frac{b-\sqrt{d}}{2}\right)^n
f ( n ) = b f ( n 1 ) + d b 2 4 f ( n 2 ) f(n)=bf(n-1)+\frac{d-b^2}{4}f(n-2)
f ( 0 ) = 2 , f ( 1 ) = b f(0)=2,f(1)=b
然后就可以随便做一下了 不难得到 f ( n ) f(n)
分类讨论后面那项即可。特判 n = 0 n=0

注意这个很丑的模数
它非常接近 long long 的上限
所以我们得用 unsigned long long
每次运算还都要取模爽吗
不服去写高精

扫描二维码关注公众号,回复: 5686344 查看本文章

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<cctype>
#include<ctime>
#include<algorithm>
using namespace std;
#define ll long long
#define cl const ll
cl p = 7528443412579576937ll;
ll b, d, n;
inline ll Add(cl& a, cl& b)
{
	return (1ull*a + 1ull*b) % p;
}
inline ll Mul(ll a, ll b)
{
	long long ret = 0;
	while (b)
	{
		if (b & 1) ret = Add(ret, a);
		a = Add(a, a);
		b >>= 1;
	}
	return ret;
}
struct Matrix
{
	ll x[2][2];
	Matrix(cl&a=0,cl&b=0,cl&c=0,cl&d=0)
	{
		x[0][0] = a;
		x[0][1] = b;
		x[1][0] = c;
		x[1][1] = d;
	}
	inline Matrix operator * (const Matrix& b)
	{
		Matrix Temp;
		for (register int i = 0; i < 2; ++i)
		{
			for (register int j = 0; j < 2; ++j)
			{
				for (register int k = 0; k < 2; ++k)
				{
					Temp.x[i][j] = Add(Temp.x[i][j], Mul(x[i][k], b.x[k][j]));
				}
			}
		}
		return Temp;
	}
	inline void operator *= (const Matrix b)
	{
		*this = *this * b;
	}
} matr;
inline Matrix qpow(Matrix x, ll k)
{
	Matrix Ret(1,0,0,1);
	while (k)
	{
		if (k & 1) Ret *= x;
		x *= x;
		k >>= 1;
	}
	return Ret;
}
Matrix trans;
int main()
{
	scanf("%lld%lld%lld", &b, &d, &n);
	if (n == 0) return putchar('1'), 0;
	matr.x[0][0] = b; matr.x[1][0] = 2;
	trans = Matrix(b, (d>>2)-Mul(b+1>>1,b-1>>1), 1, 0);
	trans = qpow(trans,n-1) * matr;
	if ((sqrt(1.0*b)!=1.0*d) && (!(n&1))) --trans.x[0][0];
	if (trans.x[0][0]<0) trans.x[0][0]+=p;
	printf("%lld", trans.x[0][0]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Estia_/article/details/88827117