Files
python/TangDou/AcWing/SegmentTree/P3373_Dynamic_XDS.cpp
HuangHai 1f397eca87 'commit'
2025-08-30 18:35:01 +08:00

130 lines
4.0 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <bits/stdc++.h>
using namespace std;
const int N = 1000010;
int n, m, p;
// 动态开点线段树
#define LL long long
#define ls tr[u].l // u节点的左子节点号
#define rs tr[u].r // u节点的右儿子节点号
#define mid ((l + r) >> 1)
struct Node {
int l, r;
// 注意:这里的[l,r]与普通线段树不同!!普通线段树中是控制的范围,动态开点线段树是左右儿子的节点号!
// 动态开点线段树中u节点的控制范围是通过函数的2,3两个参数传递过去的,不是记录在tr[u].l,tr[u].r中的!记录在tr[u].l,tr[u].r中的是左右儿子的节点号!
// 理解清楚这一点非常重要,因为后面在求区间长度是,普通线段树len(u)=(tr[u].r-tr[u].l+1),而动态开点线段树len(u)=(r-l+1),len(ls)=mid-l+1,len(rs)=r-mid
int mu, add; // 乘法标记,加法标记
LL sum; // 区间和
} tr[N << 1];
int root, idx;
// 汇总
void pushup(int u) {
tr[u].sum = (tr[ls].sum + tr[rs].sum) % p;
}
// 创建节点:节点号分配,懒标记初始化
void build(int &u) {
if (u) return;
u = ++idx;
tr[u].add = 0;
tr[u].mu = 1;
}
void pushdown(int &u, int l, int r) {
if (tr[u].add == 0 && tr[u].mu == 1) return; // 默认懒标记
build(ls), build(rs); // 左儿子创建, 右儿子创建
int &mu = tr[u].mu, &add = tr[u].add; // 此时的add懒标记,已经处理过了
tr[ls].sum = ((LL)mu * tr[ls].sum % p + (LL)(mid - l + 1) * add % p) % p;
tr[rs].sum = ((LL)mu * tr[rs].sum % p + (LL)(r - mid) * add % p) % p;
tr[ls].mu = (LL)tr[ls].mu * mu % p;
tr[rs].mu = (LL)tr[rs].mu * mu % p;
tr[ls].add = ((LL)tr[ls].add * mu % p + add) % p;
tr[rs].add = ((LL)tr[rs].add * mu % p + add) % p;
mu = 1, add = 0; // 清空懒标记
}
// 乘法
void mu(int &u, int l, int r, int L, int R, int v) {
build(u); // 动态开点
if (l >= L && r <= R) { // 如果区间被完整覆盖
tr[u].add = (LL)tr[u].add * v % p; // 比较重要的一步,add要在这里乘上v,因为后面可能要加其他的数而那些数其实是不用乘k的
tr[u].mu = (LL)tr[u].mu * v % p;
tr[u].sum = (LL)tr[u].sum * v % p;
return;
}
if (l > R || r < L) return; // 如果没有交集
// 下传懒标记
pushdown(u, l, r);
// 分裂
mu(ls, l, mid, L, R, v), mu(rs, mid + 1, r, L, R, v);
// 汇总
pushup(u);
}
// 加法
void add(int &u, int l, int r, int L, int R, int v) {
build(u); // 动态开点
if (l >= L && r <= R) { // 如果区间被完整覆盖
tr[u].add = ((LL)tr[u].add + v) % p;
tr[u].sum = ((LL)tr[u].sum + ((LL)v * ((LL)r - l + 1)) % p) % p;
return;
}
if (l > R || r < L) return; // 如果没有交集
// 下传懒标记
pushdown(u, l, r);
// 分裂
add(ls, l, mid, L, R, v), add(rs, mid + 1, r, L, R, v);
// 汇总
pushup(u);
}
// 区间查询
LL query(int u, int l, int r, int L, int R) {
if (l >= L && r <= R) return tr[u].sum; // 如果完整命中,返回我的全部
if (l > R || r < L) return 0; // 如果与我无关,返回0
pushdown(u, l, r);
return (query(ls, l, mid, L, R) % p + query(rs, mid + 1, r, L, R) % p) % p;
}
int main() {
// 文件输入输出
#ifndef ONLINE_JUDGE
freopen("P3373_2.in", "r", stdin);
#endif
// 加快读入
ios::sync_with_stdio(false), cin.tie(0);
cin >> n >> m >> p;
for (int i = 1; i <= n; i++) {
int x;
cin >> x;
add(root, 1, n, i, i, x);
}
while (m--) {
int op, l, r;
cin >> op >> l >> r;
if (op == 1) {
int k;
cin >> k;
mu(root, 1, n, l, r, k);
} else if (op == 2) {
int k;
cin >> k;
add(root, 1, n, l, r, k);
} else
printf("%lld\n", query(root, 1, n, l, r));
}
return 0;
}