paste
2026-06-20 14:43:40
发布于:浙江
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n, m;
ll arr[1000020];
struct node { // 使用结构体的形式构建线段树
ll l, r, w; // 左节点的起始点 右节点终点(l->r:p节点所包含的范围) 值
ll lg; // lazytag 懒惰标记
ll lg2; // 乘法懒标记
};
node tree[1000020 * 4]; // 线段树开辟四倍空间
int mod;
void build(ll p, ll l, ll r) { // 根节点 左节点 右节点
// 将左端点和右端点的数据放入到p节点下
tree[p].l = l;
tree[p].r = r;
tree[p].lg2 = 1; // 对于乘法懒标记初始化为1
if (l == r) { // 当前是叶子节点
tree[p].w = arr[l]; // 放入值 arr[r]
return; // 结束
}
int mid = (l + r) / 2; //(l+r)>>1 找出中间值
build(p * 2, l, mid); // 构建左子树
build(p * 2 + 1, mid + 1, r); // 构建右子树
tree[p].w = (tree[p * 2].w + tree[p * 2 + 1].w) % mod; // 回溯
}
void lan(ll p) { // 懒标记下传
tree[p * 2].w = tree[p].lg2 * tree[p * 2].w + (tree[p * 2].r - tree[p * 2].l + 1) * tree[p].lg;
tree[p * 2].w %= mod;
tree[p * 2 + 1].w = tree[p].lg2 * tree[p * 2 + 1].w + (tree[p * 2 + 1].r - tree[p * 2 + 1].l + 1) * tree[p].lg;
tree[p * 2 + 1].w %= mod;
tree[p * 2].lg2 *= tree[p].lg2;
tree[p * 2].lg2 %= mod;
tree[p * 2 + 1].lg2 *= tree[p].lg2;
tree[p * 2 + 1].lg2 %= mod;
tree[p * 2].lg = tree[p].lg2 * tree[p * 2].lg + tree[p].lg;
tree[p * 2].lg %= mod;
tree[p * 2 + 1].lg = tree[p].lg2 * tree[p * 2 + 1].lg + tree[p].lg;
tree[p * 2 + 1].lg %= mod;
tree[p].lg = 0; // 清空上一层的懒标记 已经传输完毕了
tree[p].lg2 = 1; // 乘法懒标记清空为1
}
void update(ll p, ll x, ll y, ll z) { // 区间修改 根节点 需要修改的范围 需要修改的值
// 找出需要修改的位置
if (x <= tree[p].l && y >= tree[p].r) { // tree[p].w+=z;
tree[p].w += (tree[p].r - tree[p].l + 1) * z; // 对于找到的位置进行修改(给x的位置增加) tree[p].w = z;
tree[p].lg += z; // 懒标记也要加
tree[p].w %= mod;
tree[p].lg %= mod;
return;
}
lan(p); // 懒标记下传
tree[p].w = (tree[p * 2].w + tree[p * 2 + 1].w) % mod; // 回溯
// 二分范围
int mid = (tree[p].l + tree[p].r) / 2; // 当前节点左右 去划分
// 查询当前需要找的位置是在左半边还是右半边
if (x <= mid)
update(p * 2, x, y, z);
if (y > mid)
update(p * 2 + 1, x, y, z);
tree[p].w = (tree[p * 2].w + tree[p * 2 + 1].w) % mod; // 回溯
}
void update2(ll p, ll x, ll y, ll z) { // 区间修改 根节点 需要修改的范围 需要修改的值
// 找出需要修改的位置
if (x <= tree[p].l && y >= tree[p].r) { // tree[p].w+=z;
tree[p].w *= z; // 对于当前的位置相乘
tree[p].lg *= z; // 懒标记也要乘
tree[p].lg2 *= z; // 懒标记也要乘
tree[p].w %= mod;
tree[p].lg %= mod;
tree[p].lg2 %= mod;
return;
}
lan(p); // 懒标记下传
tree[p].w = (tree[p * 2].w + tree[p * 2 + 1].w) % mod; // 回溯
// 二分范围
int mid = (tree[p].l + tree[p].r) / 2; // 当前节点左右 去划分
// 查询当前需要找的位置是在左半边还是右半边
if (x <= mid)
update2(p * 2, x, y, z);
if (y > mid)
update2(p * 2 + 1, x, y, z);
tree[p].w = (tree[p * 2].w + tree[p * 2 + 1].w) % mod; // 回溯
}
ll query(ll p, int x, int y) { // 根节点 左节点 右节点
// 当前查询的p点的区间被x和y全包含
if (x <= tree[p].l && y >= tree[p].r) {
// 返回包含的值
return tree[p].w;
}
lan(p); // 懒标记下传
// 二分范围
int mid = (tree[p].l + tree[p].r) / 2; // 当前节点左右 去划分
ll flag = 0;
if (x <= mid) {
flag += query(p * 2, x, y); // 去左子树取值
flag %= mod;
}
// 左右可能都有值需要去取
if (y > mid) {
flag += query(p * 2 + 1, x, y); // 去右子树取值
flag %= mod;
}
return flag;
}
int main() {
// 增删改查
cin >> n >> m >> mod;
for (int i = 1; i <= n; i++) {
cin >> arr[i];
}
build(1, 1, n); // 根节点所包含的范围就是全部的数据
for (int i = 1; i <= m; i++) {
int x;
cin >> x;
if (x == 1) {
ll a, b, c;
cin >> a >> b >> c;
update2(1, a, b, c);
} else if (x == 2) {
ll a, b, c;
cin >> a >> b >> c;
update(1, a, b, c);
} else {
ll a, b;
cin >> a >> b;
cout << query(1, a, b) << endl;
}
}
return 0;
}
这里空空如也




















有帮助,赞一个