题解 P5175 数列
CTime_Pup_314 · · 题解
更好的阅读体验
CTime_Pup_314的博客
P5175 数列
求二阶线性常系数齐次递推的平方和
蒟蒻不会构造矩阵,所以用了一种很暴力的做法
由于这道题意要求,下文数列下标都从 1 开始
前置知识
线性常系数齐次递推关系
若递推关系满足
且对于
则称这种递推关系为线性常系数齐次递推关系,其中
斐波那契数列就是常见的满足二阶线性常系数齐次递推关系的数列
特征方程
求解线性常系数齐次递推方法的有很多,特征方程是常见的一种
对于递推
我们构造等比数列
又另一个构造等比数列
仔细思考发现对于常数
进一步思考后感觉对于
也就是说我们可以通过一些等比数列作为基底,通过它们的线性组合,生成很多满足线性常系数齐次递推的数列
如何找到这些等比数列呢?拿二阶来说
我们不妨设等比数列
两边同时消去
这个方程叫做特征方程,通过这个我们就可以找到所有满足条件的等比数列
现在用斐波那契数列举例,特征方程为
解得
我们现在要解出
带入
所以
模意义下的根式运算
假如我们在计算过程中出现了根式,但保证根号下的数不变且结果一定不包含根式,要求在模
我们可以把根式写作类似复数的形式
我们把
下面给出几项重要的运算,基不会被取模
加法
减法
共轭
乘法
除法比较特殊,我们需要先将分母有理化,即同时将分子和分母同时乘上分母的共轭,这时分母就只剩下有理部了,再把分子部分除以分母有理部的逆元即可
除法
写出来就是
struct Complex
{
int rat, irr; // rat 有理部 irr 无理部 irr_base 基
Complex(int rat = 0, int irr = 0):rat(rat), irr(irr) {};
Complex conj() const { return Complex(rat, P-irr); }
Complex operator - (const Complex &_) const { return Complex((rat-_.rat+P)%P, (irr-_.irr+P)%P); }
Complex operator + (const Complex &_) const { return Complex((rat+_.rat)%P, (irr+_.irr)%P); }
Complex operator * (const Complex &_) const
{
Complex ret;
ret.rat = (rat*_.rat%P+irr*_.irr%P*irr_base%P)%P;
ret.irr = (rat*_.irr%P+irr*_.rat%P)%P;
return ret;
}
Complex operator / (const Complex &_) const
{
Complex a = Complex(rat, irr)*_.conj(); Complex b = _*_.conj();
a = a*Complex(qpow(b.rat, P-2), 0); // 求逆
return a;
}
};
题目主体
题干给出的是一个二阶线性常系数齐次递推数列,而让求出前
有前置知识得出这个数列的通项的是几个等比数列的线性组合,那么最后也无非是几个等比数列求和罢了
同时这道题保证了特征方程有两根和根式模运算的前提条件,所以下面做法极其暴力
设数列为
则有
而
每一部分都是等比数列,用公式
暴力代码如下,没有加任何常数优化居然跑得最快
#include <cctype>
#include <algorithm>
#include <cstdio>
#define int long long // 虽然习惯不好,但是在这题比较方便
using namespace std;
const int P = 1e9+7;
int n, t, a, b, irr_base, f1, f2;
int qpow(int a, int b)
{
int ret = 1;
for( ; b; b >>= 1, a = a*a%P) if(b&1) ret = ret*a%P;
return ret;
}
struct Complex
{
int rat, irr;
Complex(int rat = 0, int irr = 0):rat(rat), irr(irr) {};
Complex conj() const { return Complex(rat, P-irr); }
Complex operator - (const Complex &_) const { return Complex((rat-_.rat+P)%P, (irr-_.irr+P)%P); }
Complex operator + (const Complex &_) const { return Complex((rat+_.rat)%P, (irr+_.irr)%P); }
Complex operator * (const Complex &_) const
{
Complex ret;
ret.rat = (rat*_.rat%P+irr*_.irr%P*irr_base%P)%P;
ret.irr = (rat*_.irr%P+irr*_.rat%P)%P;
return ret;
}
Complex operator / (const Complex &_) const
{
Complex a = Complex(rat, irr)*_.conj(); Complex b = _*_.conj();
a = a*Complex(qpow(b.rat, P-2), 0);
return a;
}
};
Complex qpow(Complex a, int b)
{
Complex ret = Complex(1, 0);
for( ; b; b >>= 1, a = a*a) if(b&1) ret = ret*a;
return ret;
}
Complex alpha, beta, x1, x2, fn;
Complex sum1, sum2, sum3, ans;
signed main()
{
scanf("%lld", &t);
while(t--)
{
scanf("%lld%lld%lld%lld%lld", &n, &f1, &f2, &a, &b);
irr_base = (a*a%P+4*b%P)%P;
x1 = Complex(a, 1)/Complex(2, 0);
x2 = Complex(a, P-1)/Complex(2, 0);
alpha = (Complex(f2, 0)-x2*Complex(f1, 0))/(x1-x2);
beta = (x1*Complex(f1, 0)-Complex(f2, 0))/(x1-x2);
fn = alpha*qpow(x1, n-1)+beta*qpow(x2, n-1);
sum1 = (qpow(x1*x1, n)-Complex(1, 0))/(x1*x1-Complex(1, 0))*alpha*alpha;
sum2 = (qpow(x2*x2, n)-Complex(1, 0))/(x2*x2-Complex(1, 0))*beta*beta;
sum3 = (qpow(x1*x2, n)-Complex(1, 0))/(x1*x2-Complex(1, 0))*beta*alpha*Complex(2, 0);
ans = sum1+sum2+sum3;
printf("%lld\n", ans.rat);
}
return 0;
}