题解 CF718C Sasha and Array

写在前面

这道题太野蛮了!

题目传送

差的阅读体验?

Solution

需要维护两个操作,区间加和区间求和。

考虑线段树。

但是斐波那契数怎么搞?

(O(n)) 预处理?显然会爆掉。

我们知道矩阵快速幂可以在 (log x) 的时间内求出一个斐波那契数。

考虑一个及其野蛮的操作,把线段树内维护的点换成矩阵。

我们知道,对于第 (n - 1) 项,有:

[egin{bmatrix} f_{n - 1} & f_{n - 2} \ end{bmatrix} imes egin{bmatrix} 1 & 1 \ 1 & 0 end{bmatrix} = egin{bmatrix} f_{n} & f_{n - 1} \ end{bmatrix}]

(base = egin{bmatrix} 1 & 1 \ 1 & 0 end{bmatrix})

假设我们有矩阵 (z = egin{bmatrix} f_{a_i} & f_{a_i-1} end {bmatrix}) ,如果对其加上 (x) ,我们可以直接进行这么一步操作:

[egin{bmatrix} f_{a_i+x} & f_{a_i-1+x} end {bmatrix} = z imes base^x ]

注意这里的乘法指的是矩阵乘法。

怎么区间合并?直接相加就行。

我们可以手模一下,发现它满足分配律,即:

[egin{bmatrix} f_a & f_{a-1} end{bmatrix} imes base + egin{bmatrix} f_b & f_{b-1} end{bmatrix} imes base = egin{bmatrix} f_a+f_b & f_{a-1} + f_{b-1} end{bmatrix} imes base ]

所以就可以直接进行合并了!

只要我们把矩阵当成点来看,那么区间修改查询等操作就和普通的线段树没有什么区别了。

预处理复杂度 (O(sum_{i=1}^n log a_i)),单次修改和查询都是 (O(log x)),可能会带一个 (8) 的常数。所以复杂度是正确的,可以通过。

题解区好像有用通项公式直接算的,通过预处理某些东西后跑起来也是飞快。

Tips

  • lazy 要初始化为单位矩阵,push_down 时相关判断也应进行修改。
  • 注意取模。
  • (a_i <= 2) 时要单独构造,其他情况使用快速幂即可。
  • 修改的时候可以直接把 (base^x) 这个矩阵传进去,减少计算量。

实现细节看代码吧,自以为自己的马蜂很好看

Code

/*
Work by: Suzt_ilymics
Problem: 不知名屑题
Knowledge: 垃圾算法
Time: O(能过)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define int long long
#define LL long long
#define orz cout<<"lkp AK IOI!"<<endl

using namespace std;
const int MAXN = 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[MAXN];

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;
}

namespace Seg {
    #define lson i << 1
    #define rson i << 1 | 1
    struct Tree {
        Matrix sum, lazy;
    }tree[MAXN << 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::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::Modify(1, 1, n, l, r, base^k);
        } else {
            printf("%lld
", Seg::Query(1, 1, n, l, r));
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/Silymtics/p/14898122.html