You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

102 lines
3.2 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include <bits/stdc++.h>
using namespace std;
//宏定义左右儿子
#define ls u << 1
#define rs (u << 1) | 1
int n, m;
typedef long long LL;
const int N = 1e5 + 10;
int a[N];
struct Node {
int l, r;
LL sum, max;
} tr[N << 2];
void pushup(int u) {
tr[u].sum = tr[ls].sum + tr[rs].sum; //更新父节点的区间和
tr[u].max = max(tr[ls].max, tr[rs].max); //更新父节点的区间最大值
}
void build(int u, int l, int r) {
tr[u] = {l, r};
if (l == r) {
//要重视这个初始值赋值!!!
//注意:这里是a[l]或a[r],可不是a[u],u是在线段树中的节点号与原数字是不直接相关的是辅助性的东西.
//而l,r在叶子节点时l=r,比如[2,2],其实就是第2个输入的值a[2]
tr[u].max = tr[u].sum = a[l]; //叶子的话最大值区间和都是一个即a[l]
return;
}
int mid = (l + r) >> 1;
build(ls, l, mid), build(rs, mid + 1, r);
//因为有初始值赋值操作,需要向父节点汇集信息
pushup(u);
}
//单点修改
void modify(int u, int x, int v) {
//不在管理范围的修改直接返回
if (tr[u].l > x || tr[u].r < x) return;
//叶子节点命中
if (tr[u].l == tr[u].r) {
tr[u].sum = tr[u].max = v;
return;
}
//不管在左还是在右,全都进行修改,递推函数第一句会把不对的位置剔除掉
modify(ls, x, v), modify(rs, x, v);
//子节点信息修改,需要更新父节点信息
pushup(u);
}
//区间取模
void modify(int u, int l, int r, int x) {
//不在管理范围的修改直接返回
if (tr[u].l > r || tr[u].r < l) return;
if (tr[u].max < x) return; //减枝 最大值都比x小取一遍模的话原来的数字也不能变
if (tr[u].l == tr[u].r) {
tr[u].sum %= x; //暴力取模每个叶子节点对x取模
tr[u].max = tr[u].sum; //最大值肯定也变小了,因为是叶子节点,最大值就是本身
return;
}
//左改改,右改改
modify(ls, l, r, x), modify(rs, l, r, x);
//区间改完,需要向父节点推送统计信息
pushup(u);
}
//查询区间和
LL query(int u, int l, int r) {
//不在管理范围的修改直接返回
if (tr[u].l > r || tr[u].r < l) return 0;
//区间完全命中,返回结果
if (tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
//返回左右子树的查询结果
return query(ls, l, r) + query(rs, l, r);
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
//构建线段树
build(1, 1, n);
int l, r, k, x;
while (m--) {
int op;
scanf("%d", &op);
if (op == 1) {
scanf("%d%d", &l, &r);
printf("%lld\n", query(1, l, r)); //查询区间和
}
if (op == 2) {
scanf("%d%d%d", &l, &r, &x);
modify(1, l, r, x); // 区间中每个数字 % x
}
if (op == 3) {
scanf("%d%d", &k, &x);
modify(1, k, x); //单点修改第k个位置值为x
}
}
return 0;
}