Loading

CF718C Sasha and Array (线段树维护矩阵)

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;
}
posted @ 2024-03-23 20:45  Fire_Raku  阅读(4)  评论(0编辑  收藏  举报