题目大意:
给定一个数组a,以及p,表示答案需要对p取模,然后给定m次操作,每次操作包含以下几个情况:
- 格式
1 t g c
,表示把所有满足 i属于[t,g] 的 𝑎i改为 𝑎𝑖×c ;- 格式
2 t g c
表示把所有满足 i属于[t,g] 的 𝑎𝑖 改为 𝑎𝑖+c ;- 格式
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;
}
那么今天学习到此为止,下次见,有什么问题疑惑评论区留言告诉我。