2020牛客寒假算法基础集训营2——J-求函数【线段树 维护 矩阵乘法】【函数推导 + 双线段树维护参数】

题目传送门


题目描述

牛可乐有 n n 个一次函数,第 i i 个函数为 f i ( x ) = k i × x + b i f_i(x)=k_i\times x+b_i
牛可乐有 m m 次操作,每次操作为以下二者其一:

1 i k b \text{1 i k b} f i ( x ) f_i(x) 修改为 f i ( x ) = k × x + b f_i(x)=k\times x+b
2 l r \text{2 l r} f r ( f r 1 ( ( f l + 1 ( f l ( 1 ) ) ) ) ) f_r\left(f_{r-1}\left(\cdots\left(f_{l+1}\left(f_l(1)\right)\right)\cdots\right)\right)

牛可乐当然(bu)会做啦,他想考考你——
答案对 1 0 9 + 7 10^9+7 取模。


输入描述:

第一行,两个正整数 n , m n,m
第二行,n 个整数 k 1 , k 2 , , k n k_1,k_2,\dots,k_n
第三行,n 个整数 b 1 , b 2 , , b n b_1,b_2,\dots,b_n
接下来 m 行,每行一个操作,格式见上。
保证 1 n , m 2 × 1 0 5 1\leq n,m\leq 2\times 10^5 0 k i , b i < 1 0 9 + 7 ,0\leq k_i,b_i < 10^9+7


输出描述:

对于每个求值操作,输出一行一个整数,表示答案。


输入

2 3
1 1
1 0
1 2 114514 1919810
2 1 2
2 1 1


输出

2148838
2


说明

初始 f 1 ( x ) = x + 1 , f 2 ( x ) = x f_1(x)=x+1,f_2(x)=x
修改后 f 2 ( x ) = 114514 x + 1919810 f_2(x)=114514x+1919810
查询时 f 1 ( 1 ) = 2 , f 2 ( f 1 ( 1 ) ) = 2148838 f_1(1)=2,f_2(f_1(1))=2148838


题解 ( 线 ) (双线段树维护参数)

  • f r ( f r 1 ( . . . ( f l + 1 ( f l ( 1 ) ) ) . . . ) ) = i = l r k i   +   i = l r b i j = i + 1 r k j f_r(f_{r-1}(...(f_{l+1}(f_l(1)))...)) \\ =\prod_{i=l}^rk_i\ +\ \sum_{i=l}^rbi\prod_{j=i+1}^rk_j

  • i + 1 > r i + 1 > r j = i + 1 r k j = 1 注意: i+1>ri+1>r 时视 \prod_{j=i+1}^r{k_j}=1

  • 对加号左右分别用线段树维护,考虑如何合并两个相邻区间 [ l 1 , r 1 ] , [ r 1 + 1 , r 2 ] [l_1,r_1], [r1+1,r_2]

  • i = l 1 r 1 k i = n 1 , i = l 2 r 2 k i = n 2 i = l 1 r 1 b i j = i + 1 r k j = m 1 , i = l 2 r 2 b i j = i + 1 r k j = m 2 \prod_{i=l_1}^{r_1}k_i=n_1, \prod_{i=l_2}^{r_2}k_i=n_2 \\ \sum_{i=l_1}^{r_1}{b_i\prod_{j=i+1}^r{k_j}}=m_1,\sum_{i=l_2}^{r_2}{b_i\prod_{j=i+1}^r{k_j}}=m_2

  • 区间 [ l 1 , r 2 ] [l_1,r_2] k i = n 1 × n 2 \prod k_i=n_1\times n_2 b i k j = m 1 × n 2 + m 2 \sum{b_i\prod k_j}=m_1\times n_2+m_2

  • 一棵线段树维护 k i \prod k_i ,另一棵维护 b i k j \sum{b_i\prod k_j} 即可。

  • n , m n,m 同阶,时间复杂度 O ( n log n ) O(n\log n)


题解2 线 (线段树维护矩阵乘法)

  • 因为 f 2 ( f 1 ( x ) ) = k 1 k 2 x + b 1 k 2 + b 2 f2(f1(x))=k1k2x+b1k2+b2
  • 根据矩阵乘法:
  • [ 1 1 0 0 ] × [ k 1 0 b 1 1 ] = [ k 1 + b 1 1 0 0 ] \left[ \begin{matrix} 1 & 1 \\ 0 & 0 \end{matrix} \right]\times\left[ \begin{matrix} k_1 & 0 \\ b_1 & 1 \end{matrix} \right]=\left[ \begin{matrix} k_1+b_1 & 1 \\ 0 & 0 \end{matrix} \right]
  • [ k 1 + b 1 1 0 0 ] × [ k 2 0 b 2 1 ] = [ k 1 k 2 + b 1 k 2 + b 2 1 0 0 ] \left[ \begin{matrix} k_1+b_1 & 1 \\ 0 & 0 \end{matrix} \right]\times\left[ \begin{matrix} k_2 & 0 \\ b_2 & 1 \end{matrix} \right]=\left[ \begin{matrix} k_1k_2+b_1k_2+b2 & 1 \\ 0 & 0 \end{matrix} \right]
  • 可以让线段树的叶节点维护矩阵 [ k i 0 b i 1 ] \left[ \begin{matrix} k_i & 0 \\ b_i & 1 \end{matrix} \right] ,每个节点维护矩阵 l r l−r 的乘积,每次更改就只更改矩阵里的参数,每次查询就查询 l r l−r 的乘积,再用矩阵 [ 1 1 0 0 ] \left[ \begin{matrix} 1 & 1 \\ 0 & 0 \end{matrix} \right] 乘以查询的结果,结果矩阵的 [ 0 ] [ 0 ] [0][0] 就是答案了。

AC-Code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
const int maxn = 2e5 + 5;
const int mod = 1e9 + 7;

int k[maxn];
int b[maxn];
struct Mat {
	ll m[2][2];
	int left, right;
	Mat() { memset(m, 0, sizeof m); }	// 注意初始化m数组,处理下随机值
	friend Mat operator * (const Mat& a, const Mat& b) {
		Mat res;
		for (int i = 0; i < 2; ++i) 
			for (int j = 0; j < 2; ++j) {
				for (int k = 0; k < 2; ++k)
					res.m[i][j] += (a.m[i][k] * b.m[k][j]) % mod;
				res.m[i][j] %= mod;
			}
		return res;
	}
};

struct SegTree {
#define mid ((l+r)>>1)
	Mat tree[maxn << 2];
	void PushUp(int rt) {
		int l = tree[rt].left;	// 由于Mat里存储了l、r,乘法操作的时候返回新的Mat,导致l,r丢失,预先存起来,避免l、r不见了
		int r = tree[rt].right;
		tree[rt] = tree[rt << 1] * tree[rt << 1 | 1];
		tree[rt].left = l;
		tree[rt].right = r;
	}
	void build(int rt, int l, int r) {
		tree[rt].left = l;
		tree[rt].right = r;
		if (l == r) {
			tree[rt].m[0][0] = k[l];
			tree[rt].m[1][0] = b[r];
			tree[rt].m[1][1] = 1;
			return;
		}
		build(rt << 1, l, mid);
		build(rt << 1 | 1, mid + 1, r);
		PushUp(rt);
	}
	void update(int rt, int pos) {
		if (tree[rt].left == tree[rt].right) {
			tree[rt].m[0][0] = k[tree[rt].left];
			tree[rt].m[1][0] = b[tree[rt].right];
			return;
		}
		int md = (tree[rt].left + tree[rt].right) >> 1;
		if (pos <= md)	update(rt << 1, pos);
		else update(rt << 1 | 1, pos);
		PushUp(rt);
	}
	Mat query(int rt, int l, int r) {
		if (l <= tree[rt].left && tree[rt].right <= r)	return tree[rt];
		Mat res;	res.m[0][0] = res.m[1][1] = 1;	// 初始化为单位阵
		int md = (tree[rt].left + tree[rt].right) >> 1;
		if (l <= md)	res = res * query(rt << 1, l, r);
		if (r > md)	res = res * query(rt << 1 | 1, l, r);
		return res;
	}

#undef mid
};

SegTree st;
int main() {
    ios;
	int n, m;	while (cin >> n >> m) {
		for (int i = 1; i <= n; ++i)	cin >> k[i];
		for (int i = 1; i <= n; ++i)	cin >> b[i];
		st.build(1, 1, n);
		while (m--) {
			int x, i, k, b, l, r;	cin >> x;
			Mat ans;	ans.m[0][0] = ans.m[0][1] = 1;
			if (x & 1) {
				cin >> i >> k >> b;
				::k[i] = k % mod, ::b[i] = b % mod;
				st.update(1, i);
			}
			else {
				cin >> l >> r;
				ans = ans * st.query(1, l, r);
				cout << ans.m[0][0] << endl;
			}
		}
	}
	return 0;
}
发布了157 篇原创文章 · 获赞 99 · 访问量 9788

猜你喜欢

转载自blog.csdn.net/Q_1849805767/article/details/104213002
今日推荐