NC18987 粉嘤花之恋

发布时间 2023-08-27 20:14:56作者: 空白菌

题目链接

题目

题目描述

qn是个特别可爱的小哥哥,qy是个特别好的小姐姐,他们两个是一对好朋友 [ cp (划掉~)
又是一年嘤花烂漫时,小qn于是就邀请了qy去嘤花盛开的地方去玩。当qy和qn来到了田野里时,qy惊奇的发现,嘤花花瓣以肉眼可见的速度从树上长了出来。
仔细看看的话,花瓣实际上是以一定规律长出来的,而且,每次张成新的花瓣的时候,上一次的花瓣就会都落到地上,而且不会消失。
花瓣生长的规律是,当次数大于等于2时,第i次长出来的花瓣个数和上一次张出来的花瓣个数的差是斐波那契数列的第i-1项。初始的时候地上没有花瓣,树上的花瓣个数为1,第一次生长的花瓣个数为1。初始的那个花瓣就落到了地上

现在,小qn想知道,经过k次生长之后,树上和地上的总花瓣个数是多少?

ps:斐波那契数列:

​ f[1]=f[2]=1;f[i]=f[i-1]+f[i-2] (i>=2且i ∈ N+)

输入描述

一行一个数k

输出描述

一行一个数m,表示第k次生长过后,树上和地上的总花瓣数是多少。由于答案会很大,请你将答案mod 998244353后输出

示例1

输入

4

输出

12

说明

第一次:树上1,地上1.第二次树上2,地上1+1,第三次树上3,地上1+1+2,第四次树上5,地上1+1+2+3。总共12个

示例2

输入

5

输出

20

说明

第五次树上8,地上1+1+2+3+5。总共20个

备注

对于0%的数据,有k=样例
对于20%的数据,有k<=1'000
对于60%的数据,有k<=1'000'000
对于80%的数据,有k<=1'000'000'000
对于100%的数据,有k<1'000'000'000'000'000'000

题解

知识点:运算优化,线性代数。

\(f(i)\) 表示第 \(i\) 次长出来的花瓣数,\(g(i)\) 表示第 \(i\) 次生长后花瓣总数, \(F(i)\) 表示斐波那契数列第 \(i\) 项。

显然,有如下转移:

\[\begin{aligned} \begin{pmatrix} 1 &1 &0 &0\\ 0 &1 &1 &0\\ 0 &0 &1 &1\\ 0 &0 &1 &0\\ \end{pmatrix} \begin{pmatrix} g(i)\\ f(i+1)\\ F(i+1)\\ F(i)\\ \end{pmatrix} = \begin{pmatrix} g(i+1)\\ f(i+2)\\ F(i+2)\\ F(i+1)\\ \end{pmatrix} \end{aligned} \]

我们使用矩阵快速幂优化,有如下等式:

\[\begin{aligned} \begin{pmatrix} 1 &1 &0 &0\\ 0 &1 &1 &0\\ 0 &0 &1 &1\\ 0 &0 &1 &0\\ \end{pmatrix}^k \begin{pmatrix} g(0)\\ f(1)\\ F(1)\\ F(0)\\ \end{pmatrix} = \begin{pmatrix} g(k)\\ f(k+1)\\ F(k+1)\\ F(k)\\ \end{pmatrix} \end{aligned} \]

时间复杂度 \(O(\log k)\)

空间复杂度 \(O(1)\)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

struct Matrix {
    const static int P;

    int n, m;
    vector<vector<int>> mat;

    Matrix(int _n = 0) :n(_n), m(_n), mat(_n + 1, vector<int>(_n + 1)) { for (int i = 1;i <= n;i++) mat[i][i] = 1; }
    Matrix(int _n, int _m) :n(_n), m(_m), mat(_n + 1, vector<int>(_m + 1)) {}
    Matrix(const vector<vector<int>> &_mat) :n(_mat.size() - 1), m(_mat[1].size() - 1), mat(_mat) {}

    friend Matrix operator*(const Matrix &A, const Matrix &B) {
        Matrix ans(A.n, B.m);
        if (A.m != B.n) return ans;
        for (int i = 1;i <= A.n;i++)
            for (int k = 1;k <= A.m;k++) //a.m == b.n
                for (int j = 1;j <= B.m;j++)
                    ans.mat[i][j] = (ans.mat[i][j] + 1LL * A.mat[i][k] * B.mat[k][j]) % P;
        return ans;
    }

    friend Matrix operator^(Matrix A, ll k) {
        Matrix ans(A.n);
        while (k) {
            if (k & 1) ans = ans * A;
            k >>= 1;
            A = A * A;
        }
        return ans;
    }

    friend ostream &operator<<(ostream &os, const Matrix &A) {
        for (int i = 1; i <= A.n; i++)
            for (int j = 1; j <= A.m; j++)
                os << A.mat[i][j] << " \n"[i != A.n && j == A.m];
        return os;
    }
};

const int P = 998244353;
const int Matrix::P = ::P;

Matrix A(
    {
        {-1,-1,-1,-1,-1},
        {-1, 1, 1, 0, 0},
        {-1, 0, 1, 1, 0},
        {-1, 0, 0, 1, 1},
        {-1, 0, 0, 1, 0}
    }
);

Matrix B(
    {
        {-1,-1},
        {-1, 1},
        {-1, 1},
        {-1, 1},
        {-1, 0},
    }
);

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    ll n;
    cin >> n;
    cout << ((A ^ n) * B).mat[1][1] << '\n';
    return 0;
}