P5136 sequence 题解
发现要求的是带根号的,考虑二次剩余共轭。
令 \(p=\frac{1+\sqrt{5}}{2},q=\frac{1-\sqrt{5}}{2}\),显然有:
- \(p+q=1\)
- \(pq=-1\)
对于方程 \(x^2-x-1\),\(p,q\) 即为方程两根。
直接求 \(p^n\) 较为困难,考虑记 \(f_n=p^n+q^n\)。有:
根据这个递推式和上面的特征方程,发现这是一个斐波那契数列,首项可以手推:\(f_0=p^0+q^0=2,f_1=p+q=1\)。
注意到最后答案应该是 \(f_n-q^n\),发现 \(\left|q\right|<\overline\phi\),根据上取整的性质,有 \(\texttt{ans}=f_n+[n\text { is odd}]\)。
至此,可以用矩阵快速幂在 \(\Theta(T\log n)\) 的时间复杂度内解决本题。
不过根据斐波那契循环节的常识,本题还能继续优化。
发现模数 \(998244353\equiv3\pmod{5}\),则循环节为 \(2\times(998244353+1)\)(详见 斐波那契循环节)。
至此,可以通过 \(n\le10^{114514+1919810}\) 的数据了,复杂度与 \(n\) 无关(除读入)。
时间复杂度 \(\Theta(T\log 998244353)=\Theta(T)\)(雾
时间复杂度 \(\Theta(T\log p)\)。
还可以继续拓展。若 \(p\) 不为质数,则可以继续分解(详见 斐波那契循环节)求出循环节 \(G\),由于 \(G\le 6p\),时间复杂度 \(\Theta(\sqrt{p}+\log p)\sim\Theta(T\log p)\)。
更进一步,发现有多组询问,我们考虑降低一次查询的复杂度。
类似 BSGS 的思想,我们可以对矩阵幂分块,记转移矩阵为 \(P\),\(S = \sqrt{p}\),用哈希表记录 \(P^S,P^{2S},P^{3S},\cdots\) 和 \(P,P^2,\cdots,P^{S-1}\),求解时只需完成三个矩阵相互乘即可。
时间复杂度 \(\Theta(\sqrt{p}+\log p)\sim\Theta(T)\)。
CODE
// Problem: P5136 sequence
// From: Luogu
// URL: https://www.luogu.com.cn/problem/P5136
// Time: 2022-03-06 15:59
// Author: lingfunny
#include <bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/hash_policy.hpp>
using namespace __gnu_pbds;
#define LL long long
const int mod = 998244353, loop = (mod+1)<<1, S = ceil(sqrt(loop));
using namespace std;
int tt, n;
inline int mul(int x, int y) { return (LL)x*y%mod; }
struct matrix {
int a[2][2], n, m;
inline matrix operator * (const matrix& rhs) const {
matrix res;
res.n = n, res.m = rhs.m;
for(int i = 0; i < n; ++i)
for(int j = 0; j < rhs.m; ++j) {
res.a[i][j] = 0;
for(int k = 0; k < m; ++k)
(res.a[i][j] += mul(a[i][k], rhs.a[k][j])) %= mod;
}
return res;
}
} A = { { {2, 1}, {0, 0} }, 1, 2 }, ls[S+5];
const matrix P = { { {0, 1}, {1, 1} }, 2, 2 };
gp_hash_table <int, matrix> gs;
inline void init() {
matrix x = {{{1,0},{0,1}},2,2}, y;
gs[0] = x;
for(int i = 0; i < S; ++i) {
ls[i] = x;
x = x * P;
}
y = x;
for(int i = 1; i <= S; ++i) {
gs[i] = y;
y = y * x;
}
}
#define GC getchar()
inline int read() {
unsigned x = 0; char ch = GC;
while(ch < '0') ch = GC;
while('0' <= ch) { x = ((x<<1) + ((LL)x<<3) + (ch^48)) % loop; ch = GC; }
return x;
}
signed main() {
init();
tt = read();
while(tt--) {
n = read();
A.a[0][0] = 2; A.a[0][1] = 1;
A = A * (gs[n/S] * ls[n%S]);
printf("%d\n", (A.a[0][0]+(n&1))%mod);
}
return 0;
}