[洛谷P5136]sequence
题目大意:有$T(T\leqslant10^5)$组询问,每次求$A_n(n\leqslant10^{18})$:
$$
A_n=\left\lceil\left(\dfrac{\sqrt5+1}2\right)^n\right\rceil
$$
题解:通过打表看题解发现,这个序列是$2,3,5,7,\dots$,即$A_n=A_{n-1}+A_{n-2}-[n\equiv0\pmod2]$,题解中说可以记录三个信息矩阵快速幂一下,然后我并不会处理$[n\equiv0\pmod2]$(果然我最菜)
继续看下去,他说可以构造数列$F$,$F_n=F_{n-1}+F_{n-1}(F_1=1,F_2=3)$,$A_n=F_n+(n\bmod2)$,这样就可以过去了,复杂度$O(2^3T\log_2n)$
但是这样似乎感觉不够优秀,可以把转移矩阵分块预处理出来,$n\leqslant10^{18}<2^{60}$,可以$\sqrt{\sqrt n}$即$2^{15}$分一块,这样就可以在常数复杂度内求出一次的答案了,复杂度$O(4\times2^3T)$
更进一步的是,$F$为斐波那契数列,它在模一个数下有循环节,洛谷上有这么一道题,比如在$998244353$下为$1996488708$,这样就可以取模后分块,就可以只分成两块,减少常数,复杂度$O(2\times2^3T)$(但是我跑的比上一个慢。。。加了编译指令才比上一个快)
卡点:最开始以为矩阵的右下角不会有值
C++ Code:
#include <cstdio> #include <cctype> #define N 65537 const int mod = 998244353, cover = (1 << 16) - 1; namespace std { struct istream { #define M (1 << 23 | 3) char buf[M], *ch = buf - 1; inline istream() { fread(buf, 1, M, stdin); } inline istream& operator >> (int &x) { while (isspace(*++ch)); for (x = *ch & 15; isdigit(*++ch); ) x = x * 10 + (*ch & 15); return *this; } inline istream& operator >> (long long &x) { while (isspace(*++ch)); for (x = *ch & 15; isdigit(*++ch); ) x = x * 10 + (*ch & 15); return *this; } #undef M } cin; struct ostream { #define M (1 << 22 | 3) char buf[M], *ch = buf - 1; inline ostream& operator << (int x) { if (!x) {*++ch = '0'; return *this;} static int S[20], *top; top = S; while (x) {*++top = x % 10 ^ 48; x /= 10;} for (; top != S; --top) *++ch = *top; return *this; } inline ostream& operator << (const char x) {*++ch = x; return *this;} inline ~ostream() { fwrite(buf, 1, ch - buf + 1, stdout); } #undef M } cout; } struct Matrix { int s00, s01, s10, s11; Matrix() { } Matrix(int __00, int __01, int __10, int __11) : s00(__00), s01(__01), s10(__10), s11(__11) { } inline Matrix operator * (const Matrix &rhs) { #define M(l, r) static_cast<long long> (s##l) * rhs.s##r #define C(ll, lr, rl, rr) (M(ll, lr) + M(rl, rr)) % mod return Matrix(C(00, 00, 01, 10), C(00, 10, 01, 11), C(10, 00, 11, 10), C(10, 01, 11, 11)); #undef M #undef calc } } base0[N], base1[N], ans(3, 1, 0, 0); void init() { const Matrix I(1, 0, 0, 1); #define work(x) \ *base##x = I; \ for (int i = 1; i < N; ++i) base##x[i] = base##x[i - 1] * __base##x; const Matrix __base0(1, 1, 1, 0); work(0); const Matrix __base1 = base0[N - 1]; work(1); #undef work } int Tim; int main() { init(); std::cin >> Tim; while (Tim --> 0) { static long long n; static int res; std::cin >> n; --n, n %= 1996488708; res = (ans * base0[n & cover] * base1[n >> 16 & cover]).s01 + !(n & 1) - mod; std::cout << res + (res >> 31 & mod) << '\n'; } return 0; }