import java.io.*;
class Node {
int l; // 区间左端点
int r; // 区间右端点
long sum; // 区间和
long add; // 加法标记
long mul; // 乘法标记
public Node(int l, int r, long sum, long add, long mul) {
this.l = l;
this.r = r;
this.sum = sum;
this.add = add;
this.mul = mul;
}
}
public class Main {
static int n; // 数据范围 [1, n]
static int p; // 取模
static int[] array; // 原始数组
static Node[] tree; // 线段树
// 线段数树初始化
static void init() {
tree = new Node[n * 4];
for (int i = 1; i < tree.length; i++) {
tree[i] = new Node(0, 0, 0, 0, 1);
}
}
// 建树,用回溯从下往上建
static void build(int l, int r, int num) {
tree[num].l = l;
tree[num].r = r;
// 如果区间长度为 1,递归结点
if (l == r) {
tree[num].sum = array[l] % p;
return;
}
int mid = (l + r) / 2;
build(l, mid, num * 2);
build(mid + 1, r, num * 2 + 1);
update(num); // 记得更新结点的值
}
// 更新结点的值
static void update(int num) {
tree[num].sum = (tree[num * 2].sum + tree[num * 2 + 1].sum) % p;
}
// 区间修改,区间类型有三种情况
// 将区间[l, r]每个都加上 value
static void modifyAdd(int l, int r, int num, int value) {
// 如果找到要修改的区间
if (tree[num].l == l && tree[num].r == r) {
tree[num].add += value;
tree[num].sum = (tree[num].sum + (r - l + 1) * value) % p; // 添加懒惰标记,暂时不需要给儿子结点修改值
return;
}
// 注意:修改区间也要 pushDown
if (tree[num].add != 0 || tree[num].mul != 1) {
pushDown(num);
}
int mid = (tree[num].l + tree[num].r) / 2;
// 修改的区间都包含在左子树里
if (r <= mid) {
modifyAdd(l, r, num * 2, value);
}
// 修改的区间都包含在右子树里
else if (l > mid) {
modifyAdd(l, r, num * 2 + 1, value);
}
// 将要修改的区间拆开
else {
modifyAdd(l, mid, num * 2, value);
modifyAdd(mid + 1, r, num * 2 + 1, value);
}
update(num);
}
// 将区间[l, r]每个都乘上 value
static void modifyMul(int l, int r, int num, int value) {
// 如果找到要修改的区间
if (tree[num].l == l && tree[num].r == r) {
tree[num].mul = tree[num].mul * value % p;
tree[num].add = tree[num].add * value % p; // 注意:这里要乘,把乘法标记的作用加到加法标记
tree[num].sum = tree[num].sum * value % p;
return;
}
// 注意:修改区间也要 pushDown
if (tree[num].add != 0 || tree[num].mul != 1) {
pushDown(num);
}
int mid = (tree[num].l + tree[num].r) / 2;
// 修改的区间都包含在左子树里
if (r <= mid) {
modifyMul(l, r, num * 2, value);
}
// 修改的区间都包含在右子树里
else if (l > mid) {
modifyMul(l, r, num * 2 + 1, value);
}
// 将要修改的区间拆开
else {
modifyMul(l, mid, num * 2, value);
modifyMul(mid + 1, r, num * 2 + 1, value);
}
update(num);
}
// 用于将懒惰标记下传给儿子们,然后更新儿子们的值,清空当前结点的懒惰标记
// 当前结点的懒惰标记是对儿子结点的值起作用的,对自己是无关的
static void pushDown(int num) {
// 如果是叶子结点
if (tree[num].l == tree[num].r) {
tree[num].add = 0;
tree[num].mul = 1;
return;
}
// 先乘后加
// 下传乘法标记
tree[num * 2].mul = tree[num * 2].mul * tree[num].mul % p;
tree[num * 2 + 1].mul = tree[num * 2 + 1].mul * tree[num].mul % p;
// 下传加法标记,父亲的乘法标记对儿子的加法标记也是有作用的
tree[num * 2].add = tree[num * 2].add * tree[num].mul % p;
tree[num * 2 + 1].add = tree[num * 2 + 1].add * tree[num].mul % p;
tree[num * 2].add += tree[num].add;
tree[num * 2 + 1].add += tree[num].add;
// 更新儿子的值
tree[num * 2].sum = tree[num * 2].sum * tree[num].mul % p;
tree[num * 2].sum = (tree[num * 2].sum + (tree[num * 2].r - tree[num * 2].l + 1) * tree[num].add) % p;
tree[num * 2 + 1].sum = tree[num * 2 + 1].sum * tree[num].mul % p;
tree[num * 2 + 1].sum = (tree[num * 2 + 1].sum + (tree[num * 2 + 1].r - tree[num * 2 + 1].l + 1) * tree[num].add) % p;
tree[num].mul = 1;
tree[num].add = 0;
}
// 区间查询
static long query(int l, int r, int num) {
if (tree[num].add != 0 || tree[num].mul != 1) {
pushDown(num);
}
if (tree[num].l == l && tree[num].r == r) {
return tree[num].sum;
}
int mid = (tree[num].l + tree[num].r) / 2;
if (r <= mid) {
return query(l, r, num * 2);
}
if (l > mid) {
return query(l, r, num * 2 + 1);
}
return query(l, mid, num * 2) + query(mid + 1, r, num * 2 + 1);
}
public static void main(String[] args) throws IOException {
n = nextInt();
int m = nextInt(); // 总操作个数
p = nextInt(); // 模数
array = new int[n + 1];
for (int i = 1; i <= n; i++) {
array[i] = nextInt();
}
// 开始建树
init();
build(1, n, 1);
// 开始查询
for (int i = 0; i < m; i++) {
int oper = nextInt();
int x = nextInt();
int y = nextInt();
if (oper == 1) {
int k = nextInt();
modifyMul(x, y, 1, k);
}
else if (oper == 2) {
int k = nextInt();
modifyAdd(x, y, 1, k);
// System.out.println("******************");
// for (int j = 1; j <= 7; j++) {
// System.out.println(tree[j].l + " " + tree[j].r + " " + tree[j].sum + " " + tree[j].lazy);
// }
// System.out.println("*******************");
}
else if (oper == 3) {
long ans = query(x, y, 1) % p;
pw.println(ans);
}
}
pw.flush();
}
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static StreamTokenizer st = new StreamTokenizer(br);
static PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
static int nextInt() throws IOException {
st.nextToken();
return (int) st.nval;
}
}
洛谷P3373 线段树模板2 Java
最新推荐文章于 2022-08-15 03:05:15 发布