CF718C Sasha and Array

题目大意

在本题中,我们用 f i f_i fi 来表示第 i i i 个斐波那契数 f 1 = f 2 = 1 , f i = f i − 1 + f i − 2 ( i ≥ 3 ) f_1=f_2=1,f_i=f_{i-1}+f_{i-2}(i\ge 3) f1=f2=1,fi=fi1+fi2(i3)

给定一个 n n n 个数的序列 a a a

m m m 次操作,操作有两种:

  • a l ∼ a r a_l\sim a_r alar 加上 x x x

  • ( ∑ i = l r f a i )   m o d   ( 1 0 9 + 7 ) \displaystyle\left(\sum_{i=l}^r f_{a_i}\right)\bmod (10^9+7) (i=lrfai)mod(109+7)

1 ≤ n , m ≤ 1 0 5 1\le n,m\le 10^5 1n,m105 1 ≤ a i ≤ 1 0 9 1\le a_i\le 10^9 1ai109

解题思路

两个操作,区间加,区间求和。

显然线段树。

斐波那契数怎么搞?

O ( n ) \mathcal O(n) O(n) 预处理显然爆。

显然用 O ( log ⁡ n ) \mathcal O(\log n) O(logn) 的矩阵快速幂来求。

b a s e = ( 1 1 1 0 ) base=\begin{pmatrix}1&1\\1&0\end{pmatrix} base=(1110),则有 [ f n f n − 1 f n − 1 f n − 2 ] = ( 1 0 0 1 ) ∗ b a s e n − 1 \begin{bmatrix} f_n && f_{n-1} \\ f_{n-1} && f_{n-2}\end{bmatrix}=\begin{pmatrix}1&0\\0&1\end{pmatrix} * base^{n-1} [fnfn1fn1fn2]=(1001)basen1

由于矩阵具有分配律,即 a × b + a × c = a × ( b + c ) a\times b+a\times c=a\times(b+c) a×b+a×c=a×(b+c),所以对于一段区间的矩阵可以相加维护,所以 ∑ i = l r f ( a i + x ) = f x ∗ ∑ i = l r ( a i ) \sum\limits_{i=l}^{r}f(a_i+x)=f_x*\sum\limits_{i=l}^{r}(a_i) i=lrf(ai+x)=fxi=lr(ai)

线段树满足所有条件!!!

线段树上每个点都搞成一个矩阵即可。

具体请看代码。

CODE

#include <bits/stdc++.h>

#define int long long

inline int read()
{
    int s = 0, f = 0;
    char ch = getchar();
    while (!isdigit(ch))
        f |= (ch == '-'), ch = getchar();
    while (isdigit(ch))
        s = (s << 1) + (s << 3) + ch - '0', ch = getchar();
    return f ? -s : s;
}

using namespace std;

const int _ = 1e5 + 5;

const int INF = 1e9 + 7;

const int mod = 1e9 + 7;

struct Matrix
{
    int a[3][3];
    Matrix() { memset(a, false, sizeof a); }
    void Init() { a[1][1] = a[2][2] = 1, a[1][2] = a[2][1] = 0; }
    bool pd() { return a[1][1] == 1 && a[2][2] == 1 && a[1][2] == 0 && a[2][1] == 0; }
    Matrix operator+(const Matrix b)
    {
        Matrix res;
        for (int i = 1; i <= 2; ++i)
            for (int j = 1; j <= 2; ++j)
                res.a[i][j] = (a[i][j] + b.a[i][j]) % mod;
        return res;
    }
    Matrix operator*(const Matrix b)
    {
        Matrix res;
        for (int k = 1; k <= 2; ++k)
            for (int i = 1; i <= 2; ++i)
                for (int j = 1; j <= 2; ++j)
                    res.a[i][j] = (res.a[i][j] + a[i][k] * b.a[k][j]) % mod;
        return res;
    }
    Matrix operator^(int b)
    {
        Matrix res, Base;
        for (int i = 1; i <= 2; ++i)
            res.a[i][i] = 1;
        for (int i = 1; i <= 2; ++i)
            for (int j = 1; j <= 2; ++j)
                Base.a[i][j] = a[i][j];
        while (b)
        {
            if (b & 1)
                res = res * Base;
            Base = Base * Base;
            b >>= 1;
        }
        return res;
    }
} base, ans;

int n, m, a[_];

namespace Seg_tree
{
#define lson i << 1
#define rson i << 1 | 1
    struct Tree
    {
        Matrix sum, lazy;
    } tree[_ << 2];
    void Push_up(int i) { tree[i].sum = tree[lson].sum + tree[rson].sum; }
    void Build(int i, int l, int r)
    {
        tree[i].lazy.Init();
        if (l == r)
        {
            if (a[l] == 1)
                tree[i].sum.a[1][1] = 1;
            else if (a[l] == 2)
                tree[i].sum.a[1][1] = tree[i].sum.a[1][2] = 1;
            else
                tree[i].sum = ans * (base ^ (a[l] - 2));
            return;
        }
        int mid = (l + r) >> 1;
        Build(lson, l, mid), Build(rson, mid + 1, r);
        Push_up(i);
    }
    void Push_down(int i)
    {
        if (tree[i].lazy.pd())
            return;
        tree[lson].lazy = tree[lson].lazy * tree[i].lazy;
        tree[rson].lazy = tree[rson].lazy * tree[i].lazy;
        tree[lson].sum = tree[lson].sum * tree[i].lazy;
        tree[rson].sum = tree[rson].sum * tree[i].lazy;
        tree[i].lazy.Init();
    }
    void Modify(int i, int l, int r, int L, int R, Matrix k)
    {
        if (L <= l && r <= R)
        {
            tree[i].lazy = tree[i].lazy * k, tree[i].sum = tree[i].sum * k;
            return;
        }
        Push_down(i);
        int mid = (l + r) >> 1;
        if (mid >= L)
            Modify(lson, l, mid, L, R, k);
        if (mid < R)
            Modify(rson, mid + 1, r, L, R, k);
        Push_up(i);
    }
    int Query(int i, int l, int r, int L, int R)
    {
        if (L <= l && r <= R)
            return tree[i].sum.a[1][1];
        Push_down(i);
        int mid = (l + r) >> 1, ans = 0;
        if (mid >= L)
            ans = (ans + Query(lson, l, mid, L, R)) % mod;
        if (mid < R)
            ans = (ans + Query(rson, mid + 1, r, L, R)) % mod;
        return ans;
    }
}

signed main()
{
    base.a[1][1] = base.a[1][2] = base.a[2][1] = 1;
    ans.a[1][1] = ans.a[1][2] = 1;
    n = read(), m = read();
    for (int i = 1; i <= n; ++i)
        a[i] = read();
    Seg_tree::Build(1, 1, n);
    for (int i = 1, opt, l, r, k; i <= m; ++i)
    {
        opt = read(), l = read(), r = read();
        if (opt == 1)
        {
            k = read();
            Seg_tree::Modify(1, 1, n, l, r, base ^ k);
        }
        else
        {
            printf("%lld\n", Seg_tree::Query(1, 1, n, l, r));
        }
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值