• 题解 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;
    }
    
  • 相关阅读:
    从命令行接收多个数字,求和之后输出结果(Java)
    《大道至简》第一章——编程的精义_读后感(Java伪代码)
    《构建之法》阅读笔记03
    《构建之法》阅读笔记02
    《构建之法》阅读笔记01
    Javaweb所用到的相关技术综述
    TomCat的安装与配置
    心形曲线
    异常处理---动手动脑及课后作业
    多态---课后作业
  • 原文地址:https://www.cnblogs.com/Silymtics/p/14898122.html
Copyright © 2020-2023  润新知