• [CF718C] Sasha and Array


    [CF718C] Sasha and Array - 线段树,矩阵乘法

    Description

    (f_i) 来表示第 (i) 个斐波那契数,给定一个 (n) 个数的序列 (a)。有 (m) 次操作,操作有两种:将 (a_lsim a_r) 加上 (x);求 (displaystyleleft(sum_{i=l}^r f_{a_i} ight)mod (10^9+7))

    Solution

    如果我们用线段树维护矩阵的和,那么每次区间修改操作可以视作区间矩阵乘法

    #include <bits/stdc++.h>
    using namespace std;
    
    #define int long long
    
    const int mod = 1e9 + 7;
    const int N = 1e6 + 5;
    
    int n, a[N];
    
    // todo: node (mat)
    
    struct Node
    {
        int a, b, c, d;
        Node operator+(const Node &rhs)
        {
            return {(a + rhs.a) % mod, (b + rhs.b) % mod, (c + rhs.c) % mod, (d + rhs.d) % mod};
        }
        Node operator*(const Node &rhs)
        {
            return {(a * rhs.a + b * rhs.c) % mod,
                    (a * rhs.b + b * rhs.d) % mod,
                    (c * rhs.a + d * rhs.c) % mod,
                    (c * rhs.b + d * rhs.d) % mod};
        }
        Node &operator*=(const Node &rhs)
        {
            return (*this) = (*this) * rhs;
        }
        bool operator!=(const Node &rhs)
        {
            return a != rhs.a || b != rhs.b || c != rhs.c || d != rhs.d;
        }
    } nodes[N], tag[N];
    
    Node qpow(Node p, int q)
    {
        return (q & 1 ? p : (Node){1, 0, 0, 1}) * (q ? qpow(p * p, q / 2) : (Node){1, 0, 0, 1});
    }
    
    // todo: segment tree (modify, query sum)
    
    void pushup(int p)
    {
        nodes[p] = nodes[p * 2] + nodes[p * 2 + 1];
    }
    
    void pushdown(int p)
    {
        if (tag[p] != (Node){1, 0, 0, 1})
        {
            nodes[p * 2] *= tag[p];
            nodes[p * 2 + 1] *= tag[p];
            tag[p * 2] *= tag[p];
            tag[p * 2 + 1] *= tag[p];
            tag[p] = {1, 0, 0, 1};
        }
    }
    
    void build(int p, int l, int r, int *a)
    {
        tag[p] = {1, 0, 0, 1};
        if (l == r)
        {
            nodes[p] = qpow({0, 1, 1, 1}, a[l]);
        }
        else
        {
            build(p * 2, l, (l + r) / 2, a);
            build(p * 2 + 1, (l + r) / 2 + 1, r, a);
            pushup(p);
        }
    }
    
    void modify(int p, int l, int r, int ql, int qr, int val)
    {
        if (l > qr || r < ql)
            return;
        if (l >= ql && r <= qr)
        {
            auto x = qpow({0, 1, 1, 1}, val);
            nodes[p] *= x;
            tag[p] *= x;
        }
        else
        {
            pushdown(p);
            modify(p * 2, l, (l + r) / 2, ql, qr, val);
            modify(p * 2 + 1, (l + r) / 2 + 1, r, ql, qr, val);
            pushup(p);
        }
    }
    
    int query(int p, int l, int r, int ql, int qr)
    {
        if (l > qr || r < ql)
            return 0;
        if (l >= ql && r <= qr)
            return nodes[p].b;
        pushdown(p);
        return query(p * 2, l, (l + r) / 2, ql, qr) + query(p * 2 + 1, (l + r) / 2 + 1, r, ql, qr);
    }
    
    int query(int ql, int qr)
    {
        return query(1, 1, n, ql, qr);
    }
    
    // todo: main
    
    signed main()
    {
        ios::sync_with_stdio(false);
    
        int m;
        cin >> n >> m;
        for (int i = 1; i <= n; i++)
            cin >> a[i];
        build(1, 1, n, a);
    
        for (int i = 1; i <= m; i++)
        {
            int t1, t2, t3, t4;
            cin >> t1 >> t2 >> t3;
            if (t1 == 1)
            {
                cin >> t4;
                modify(1, 1, n, t2, t3, t4);
            }
            else
            {
                cout << query(t2, t3) % mod << endl;
            }
        }
    }
    
  • 相关阅读:
    下载flash我的三种方法
    随机变换背景图象(一个可以刷新心情的特效)
    禁止缓存
    [模板]字符串算法
    [学习笔记]有上下界的网络流
    [bzoj2809][Apio2012]dispatching
    [四校联考]Easy Problems
    [学习笔记]tarjan
    [vijos1780][NOIP2012]开车旅行
    记第一次打女队
  • 原文地址:https://www.cnblogs.com/mollnn/p/14629283.html
Copyright © 2020-2023  润新知