「Sasha and Array」Solution

简述题意

  • 在本题中,我们用 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 次操作,操作有两种:

    1. a l ∼ a r a_l\sim a_r alar 加上 x x x
    2. ( ∑ 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

思路

比较简单的一道紫题。

看到斐波那契数列,而且涉及到 1 0 9 10^9 109 项甚至更多,首先想到矩阵快速幂。看到序列操作,又很自然的想到线段树。那么这道题基本上就做完了。

由于矩阵满足 a × ( b + c ) = a × b + a × c a \times (b+c)=a \times b + a \times c a×(b+c)=a×b+a×c,因此对于区间修改而言,我们只需像普通线段树一样,维护懒标记,然后下放即可。对于区间合并而言,直接将左右区间的答案累和就好了。注意这里懒标记以及和的类型都应该是 Matrix \text{Matrix} Matrix

我们已知斐波那契的初始矩阵 A = ( 0 1 ) A=\begin{pmatrix}0 & 1\end{pmatrix} A=(01),加速矩阵为 B = ( 0 1 1 1 ) B = \begin{pmatrix} 0 & 1 \\ 1 & 1 \end{pmatrix} B=(0111),对于每次修改,我们只需要将待修改元素的累和矩阵以及懒标记矩阵乘上 B x B^x Bx 即可。

注意懒标记要初始成单位矩阵,即 B = ( 1 0 0 1 ) B = \begin{pmatrix} 1 & 0 \\ 0 & 1 \end{pmatrix} B=(1001)

代码

如果上述有不理解的地方,看完代码应该就懂了。

#include<bits/stdc++.h>
#define int long long
const int MAXN = 1e5 + 5 , MOD = 1e9 + 7;
using namespace std;
int n , q , a[MAXN];
struct Matrix{ // 矩阵快速幂模板
	int n , m , res[3][3];
    Matrix() {memset(res , 0 , sizeof(res));}
	Matrix operator * (const Matrix &b) {
		Matrix tmp;
		tmp.n = n , tmp.m = b.m;
		for (int i = 1 ; i <= n ; i ++) {
			for (int j = 1 ; j <= b.m ; j ++) {
				for (int k = 1 ; k <= m ; k ++) {
					tmp.res[i][j] = (tmp.res[i][j] + res[i][k] * b.res[k][j] % MOD + MOD) % MOD;
				} 
			}
		}
		return tmp;
	}	
    Matrix operator ^ (int k) {
        Matrix tmp , base = (*this);
        tmp.n = tmp.m = m;
        for (int i = 1 ; i <= m ; i ++) tmp.res[i][i] = 1;
        while(k) {
            if (k & 1) tmp = tmp * base;
            base = base * base;
            k >>= 1;
        }
        return tmp;
	}
    Matrix operator + (const Matrix &b) {
        Matrix tmp;
        tmp.n = n , tmp.m = m;
        for (int i = 1 ; i <= n ; i ++) {
            for (int j = 1 ; j <= m ; j ++) {
                tmp.res[i][j] = (res[i][j] + b.res[i][j]) % MOD;
            }
        }
        return tmp;
    }
}Quick;
namespace Segment{
    struct tree{
        Matrix sum , add;
        int l , r;
    }tree[MAXN << 3];
    void pushup(int p) {tree[p].sum = tree[p << 1].sum + tree[p << 1 | 1].sum;} // 上传答案
    void pushdown(int p) { // 下放懒标记(矩阵运算满足结合律)
        tree[p << 1].add = tree[p << 1].add * tree[p].add , tree[p << 1 | 1].add = tree[p << 1 | 1].add * tree[p].add;
        tree[p << 1].sum = tree[p << 1].sum * tree[p].add , tree[p << 1 | 1].sum = tree[p << 1 | 1].sum * tree[p].add;
        tree[p].add.res[1][1] = tree[p].add.res[2][2] = 1 , tree[p].add.res[1][2] = tree[p].add.res[2][1] = 0;
    }
    void build(int p , int l , int r) {
        tree[p].l = l , tree[p].r = r , tree[p].add.res[1][1] = tree[p].add.res[2][2] = 1; // 初始化成单位矩阵
        tree[p].add.n = tree[p].add.m = 2 , tree[p].sum.n = 1 , tree[p].sum.m = 2;
        if (l == r) {
            tree[p].sum.res[1][1] = 0 , tree[p].sum.res[1][2] = 1;
            tree[p].sum = tree[p].sum * (Quick ^ a[l]); // 求斐波那契的第 a[l] 项
            return;
        }
        int mid = l + r >> 1;
        build(p << 1 , l , mid) , build(p << 1 | 1 , mid + 1 , r);
        pushup(p);
    }
    void update(int p , int l , int r , Matrix v) {
        if (tree[p].l >= l && tree[p].r <= r) {
            tree[p].add = tree[p].add * v , tree[p].sum = tree[p].sum * v;
            return;
        }
        pushdown(p);
        int mid = tree[p].l + tree[p].r >> 1;
        if (l <= mid) update(p << 1 , l , r , v);
        if (r > mid) update(p << 1 | 1 , l , r , v);
        pushup(p);
    }
    int query(int p , int l , int r) {
        if (tree[p].l >= l && tree[p].r <= r) {return tree[p].sum.res[1][1];}
        pushdown(p); 
        int mid = tree[p].l + tree[p].r >> 1 , tmp = 0;
        if (l <= mid) tmp = (tmp + query(p << 1 , l , r)) % MOD;
        if (r > mid) tmp = (tmp + query(p << 1 | 1 , l , r)) % MOD;
        return tmp;
    }
}
using namespace Segment;
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr) , cout.tie(nullptr);
    cin >> n >> q;
    Quick.n = Quick.m = 2 , Quick.res[1][2] = Quick.res[2][1] = Quick.res[2][2] = 1; // 加速矩阵
    for (int i = 1 ; i <= n ; i ++) cin >> a[i];
    build(1 , 1 , n);
    while(q --) {
        int opt , l , r , x;
        cin >> opt >> l >> r;
        if (opt == 1) cin >> x , update(1 , l , r , Quick ^ x);
        else cout << query(1 , l , r) << '\n';
    }
	return 0;
}
posted @ 2024-04-08 23:55  Fracture_Dream  阅读(5)  评论(0)    收藏  举报  来源