CF718C Sasha and Array (线段树维护矩阵)
典题,但还是第一次见。
首先看到斐波那契数列,可以想到矩阵快速幂。
想到这一点之后,很大一部分都解决了。对于修改操作,实际上就是乘以一个矩阵。对于查询,就是矩阵加法。
考虑用线段树维护矩阵。首先区间和可以矩阵加法直接做。由于我们需要区间乘,需要考虑如何下传标记,然后发现矩阵满足分配律,即 \(A\times C+B\times C=(A+B)\times C\)。所以在维护好的区间和上直接乘上转移矩阵就行。同时下传标记由于矩阵满足分配律也是可以合并的,懒标记相乘即可。
#include <bits/stdc++.h>
#define pii std::pair<int, int>
#define fi first
#define se second
#define int long long
const int N = 100010, mod = 1e9 + 7;
int a[N];
struct Mat {
int c[2][2];
void clear() {
for(int i = 0; i < 2; i++) {
for(int j = 0; j < 2; j++) {
c[i][j] = 0;
}
}
}
void re() {
c[0][0] = c[1][1] = 1, c[1][0] = c[0][1] = 0;
}
bool empty() {
return (c[0][0] == 1 && c[0][1] == 0 && c[1][0] == 0 && c[1][1] == 1);
}
friend Mat operator + (const Mat& a, const Mat& b) {
Mat ret;
ret.clear();
for(int i = 0; i < 2; i++) {
for(int j = 0; j < 2; j++) {
ret.c[i][j] = (a.c[i][j] + b.c[i][j]) % mod;
}
}
return ret;
}
friend Mat operator * (const Mat& a, const Mat& b) {
Mat ret;
ret.clear();
for(int i = 0; i < 2; i++) {
for(int j = 0; j < 2; j++) {
for(int k = 0; k < 2; k++) {
ret.c[i][j] = (ret.c[i][j] + a.c[i][k] * b.c[k][j]) % mod;
}
}
}
return ret;
}
friend Mat operator ^ (Mat a, int b) {
Mat ret;
ret.re();
while(b) {
if(b & 1) ret = ret * a;
a = a * a;
b >>= 1;
}
return ret;
}
} tmp;
struct seg {
Mat m, lzy;
} t[N << 2];
void pushup(int u) {
t[u].m = t[u << 1].m + t[u << 1 | 1].m;
}
void build(int u, int l, int r) {
t[u].lzy.c[0][0] = t[u].lzy.c[1][1] = 1;
if(l == r) {
if(a[l] == 1) {
t[u].m.c[0][0] = 1;
} else {
t[u].m.c[0][0] = t[u].m.c[0][1] = 1;
}
if(a[l] > 2) t[u].m = t[u].m * (tmp ^ (a[l] - 2));
return;
}
int mid = (l + r) >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u);
}
void pushdown(int u) {
if(t[u].lzy.empty()) return;
t[u << 1].m = t[u << 1].m * t[u].lzy, t[u << 1 | 1].m = t[u << 1 | 1].m * t[u].lzy;
t[u << 1].lzy = t[u << 1].lzy * t[u].lzy, t[u << 1 | 1].lzy = t[u << 1 | 1].lzy * t[u].lzy;
t[u].lzy.re();
}
void update(int u, int l, int r, int L, int R, Mat x) {
if(L <= l && r <= R) {
t[u].m = t[u].m * x;
t[u].lzy = t[u].lzy * x;
return;
}
int mid = (l + r) >> 1;
pushdown(u);
if(L <= mid) update(u << 1, l, mid, L, R, x);
if(R > mid) update(u << 1 | 1, mid + 1, r, L, R, x);
pushup(u);
}
Mat query(int u, int l, int r, int L, int R) {
if(L <= l && r <= R) {
return t[u].m;
}
int mid = (l + r) >> 1;
pushdown(u);
if(R <= mid) return query(u << 1, l, mid, L, R);
if(L > mid) return query(u << 1 | 1, mid + 1, r, L, R);
Mat ret;
ret.clear();
ret = query(u << 1, l, mid, L, R) + query(u << 1 | 1, mid + 1, r, L, R);
return ret;
}
void Solve() {
tmp.c[0][0] = tmp.c[0][1] = tmp.c[1][0] = 1, tmp.c[1][1] = 0;
int n, m;
std::cin >> n >> m;
for(int i = 1; i <= n; i++) {
std::cin >> a[i];
}
build(1, 1, n);
while(m--) {
int op, l, r;
std::cin >> op >> l >> r;
if(op == 1) {
int x;
std::cin >> x;
update(1, 1, n, l, r, tmp ^ x);
} else {
std::cout << query(1, 1, n, l, r).c[0][0] % mod << "\n";
}
}
}
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
Solve();
return 0;
}