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