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

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

Description

用 \(f_i\) 来表示第 \(i\) 个斐波那契数,给定一个 \(n\) 个数的序列 \(a\)。有 \(m\) 次操作,操作有两种:将 \(a_l\sim a_r\) 加上 \(x\);求 \(\displaystyle\left(\sum_{i=l}^r f_{a_i}\right)\bmod (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;
        }
    }
}
上一篇:尽量不要在JS中使用位运算


下一篇:iOS 静态库生成(引用第三方SDK、开源库、资源包)