Bootstrap

洛谷P2023 [AHOI2009] 维护序列 线段树2模板

题目传送门

题目大意:

给定一个数组a,以及p,表示答案需要对p取模,然后给定m次操作,每次操作包含以下几个情况:

  1. 格式 1 t g c,表示把所有满足 i属于[t,g] 的 𝑎i改为 𝑎𝑖×c ;
  2. 格式 2 t g c 表示把所有满足  i属于[t,g] 的 𝑎𝑖​ 改为 𝑎𝑖+c ;
  3. 格式 3 t g 询问所有满足  i属于[t,g]的 𝑎𝑖​ 的和,由于答案可能很大,你只需输出这个数模 p 的值。

该题为线段树模板2,也就是有多种更改要求的情况,这种情况和模板1可以说是一模一样,只是需要考虑一下更新数据的先后顺序,该题很显然是先乘后加的因为加法对乘法是没有影响的。这个可以在草稿纸上演算一遍,例如a=[1,2,1,5,3],将[2,4]区间的先加1,再乘以3,可以得到最后a=[1,9,6,18,3]

void laze(ll i, ll pl, ll pr, ll sum, ll cheng) {
	tagg[i] = (tagg[i] * cheng) % mod;
	tree[i] = (tree[i] * cheng) % mod;
	tag[i] = (tag[i] * cheng + sum) % mod;
	tree[i] = (tree[i] + (pr - pl + 1) * sum % mod) % mod;
}

若按照上面这个代码操作,可以得到相同答案。

接下来直接看全部代码:使用两个懒标记数组分别标记sum和cheng就行了

#include<bits/stdc++.h>

using namespace std;
using ll = long long;
const ll N = 1e6 + 5;

ll tree[N << 2], tag[N << 2], tagg[N << 2]; //线段树空间浪费是必须的

int a[N];
ll n, m, mod;

int lson(int i) { //访问左儿子
	return i << 1;
}
int rson(int i) { //访问右儿子
	return i << 1 | 1;
}

void up(int i) {//汇合子树数据
	tree[i] = (tree[lson(i)] + tree[rson(i)]) % mod;
}

void build(ll i, ll pl, ll pr) { //该题同样无需建树,可以忽略不用
	tag[i] = 0;
	tagg[i] = 1;
	if (pl == pr) { //说明到了叶子节点
		tree[i] = a[pl];
		return ;
	}
	int mid = (pl + pr) >> 1; //分治,继续查节点
	build(lson(i), pl, mid);
	build(rson(i), mid + 1, pr);
	up(i);//自底向上传递区间值
}

void laze(ll i, ll pl, ll pr, ll sum, ll cheng) {
	tagg[i] = (tagg[i] * cheng) % mod;
	tree[i] = (tree[i] * cheng) % mod;
	tag[i] = (tag[i] * cheng + sum) % mod;
	tree[i] = (tree[i] + (pr - pl + 1) * sum % mod) % mod;
}

void down(ll i, ll pl, ll pr) {//下发任务
	if (tag[i] || tagg[i] != 1) {
		ll mid = (pl + pr) >> 1;
		laze(lson(i), pl, mid, tag[i], tagg[i]);
		laze(rson(i), mid + 1, pr, tag[i], tagg[i]);
		tag[i] = 0;
		tagg[i] = 1;
	}
}

void update(ll i, ll pl, ll pr, ll L, ll R, ll sum, ll cheng) { //在[pl,pr]里面,更新[L,R]区间的数
	if (L <= pl && pr <= R) {
		laze(i, pl, pr, sum, cheng); //利用懒信息拦住,避免不必要的运行
		//下一次修改时,需要用再往下发放懒信息
		return ;
	}
	down(i, pl, pr); //不能覆盖的话就看看有没有懒信息需要下发
	//把懒信息传给子树
	ll mid = (pl + pr) >> 1;
	if (L <= mid) {//只要有被包含的范围,就需要查
		update(lson(i), pl, mid, L, R, sum, cheng);
	}
	if (R > mid) {
		update(rson(i), mid + 1, pr, L, R, sum, cheng);
	}
	up(i);
}

//查询函数和更新数据函数很像
ll querr(ll i, ll pl, ll pr, ll L, ll R) { //在[pl,pr]里面,查询[L,R]区间的和
	if (L <= pl && pr <= R) {//若全部覆盖,直接返回该处的tree[i]
		return tree[i];
	}
	ll res = 0;
	down(i, pl, pr); //不能覆盖,将懒信息下发,递归子树
	ll mid = (pl + pr) >> 1;
	if (L <= mid) {
		res += (querr(lson(i), pl, mid, L, R)) % mod;
	}
	if (R > mid) {
		res += (querr(rson(i), mid + 1, pr, L, R)) % mod;
	}
	return res % mod;
}

int main() {
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);

	cin >> n >> mod;
	for (int i = 1; i <= n; i++)cin >> a[i];
	build(1, 1, n);
	cin >> m;
	while (m--) {
		ll op, t, g, c;
		cin >> op;
		if (op == 1) {
			cin >> t >> g >> c;
			update(1, 1, n, t, g, 0, c);
		} else if (op == 2) {
			cin >> t >> g >> c;
			update(1, 1, n, t, g, c, 1);
		} else {
			cin >> t >> g;
			cout << querr(1, 1, n, t, g) % mod << '\n';
		}

	}
	return 0;
}

那么今天学习到此为止,下次见,有什么问题疑惑评论区留言告诉我。

;