题目链接
题目描述
有一个长度为 \(n\) 的数列 \(\{a_n\}\) 和 \(m\) 次操作,操作内容如下:
- 格式为
1 l r
,表示求 \(\sum \limits _{i=l}^{r} a_i\) 的值并输出。 - 格式为
2 l r x
,表示对区间 \([l,r]\) 内每个数取模,模数为 \(x\)。 - 格式为
3 k x
,表示将 \(a_k\) 修改为 \(x\)。
\(1 \leq n, m \leq 10^5\) , \(1 \leq l, r, k, x, a_i \leq 10^9\)
解题思路
通过题目中两个区间操作不难发现此题需要用 线段树 维护, 本题解着重讲解操作 \(2\)。
-
对于操作 \(1\) :线段树可以轻松维护区间和的查询和维护。
-
对于操作 \(3\) :线段树也支持单点修改。
- 对于操作 \(1\) 和操作 \(3\) ,可以先利用线段树做出 P3374 【模板】树状数组 1 这题,如果实在做不出来
那你线段树学了个啥可以去参考一下题解 。
- 对于操作 \(1\) 和操作 \(3\) ,可以先利用线段树做出 P3374 【模板】树状数组 1 这题,如果实在做不出来
-
对于操作 \(2\) :我们可以探究一下 正整数 之间取模的性质:
-
我们设 \(p\) 为模数, \(x\) 为被模数,关于 \(x\ \text{mod}\ p\) 的值有以下几点性质:
- 性质 \(1\) :当 \(x < p\) 时:\(x\ \text{mod}\ p = x\) 。
- 性质 \(2\) :当 \(x \geq p\) 时: \(x\ \text{mod}\ p < \dfrac{x}{2}\) 。
所以对于性质 \(1\) 我们发现所有小于模数 \(p\) 的被模数 \(x\) 对答案没有影响,所以我们可以用线段树维护一个区间 \(max\) ,如果区间 \(max\) 大于模数 \(p\) 我们可以选择不修改。对于性质 \(2\) ,每次取模都会使 \(x\) 至少缩减 \(\frac{1}{2}\) ,所以至多修改 \(\log_2 x\) 次,所以对于区间的修改我们以选择暴力的对区间中的每个节点修改。
-
对于性质 \(2\) ,我们给出证明:
设 \(x = kp + c\) ,(\(c\) 表示 \(x\ \text{mod}\ p\) 的值,\(p\) 为模数, \(kp\) 表示小于等于 \(x\) 可以被 \(p\) 整除的最大正整数)
根据取模性质得出 \(c < p\) ,又因为 \(p \in \text{N}^* , kp \in \text{N}^*\) ,所以 \(k \in \text{N}^*\) ,所以 \(k \geq 1\) 。
然后我们就得出以下式子:
-
代码:
#include <bits/stdc++.h>const int N = 100010;int n, m;
long long a[N];class SegmentTree {
public:void pushup(int p) {t[p].sum = t[p << 1].sum + t[(p << 1) + 1].sum;t[p].max = std::max(t[p << 1].max, t[(p << 1) + 1].max);}void build(int p, int l, int r) {t[p].l = l;t[p].r = r;if (l == r) {t[p].sum = a[l];t[p].max = a[l];return;}int mid = (t[p].l + t[p].r) >> 1;build(p << 1, l, mid);build((p << 1) + 1, mid + 1, r);pushup(p);}void change_cover(int p, int x, long long k) {if (t[p].l == t[p].r) {t[p].sum = k;t[p].max = k;return;}int mid = (t[p].l + t[p].r) >> 1;if (x <= mid) change_cover(p << 1, x, k);else change_cover((p << 1) + 1, x, k);pushup(p);}void change_mod(int p, int l, int r, long long k) {if (t[p].max < k) return;if (t[p].l == t[p].r) {t[p].sum %= k;t[p].max %= k;return;}int mid = (t[p].l + t[p].r) >> 1;if (l <= mid) change_mod(p << 1, l, r, k);if (r > mid) change_mod((p << 1) + 1, l, r, k);pushup(p);}long long ask(int p, int l, int r) {if (l <= t[p].l && r >= t[p].r)return t[p].sum;long long sum = 0;int mid = (t[p].l + t[p].r) >> 1;if (l <= mid) sum += ask(p << 1, l, r);if (r > mid) sum += ask((p << 1) + 1, l, r);return sum;}
private:struct point {int l, r;long long sum, max;} t[N << 2];
} t;int main() {scanf("%d%d", &n, &m);for (int i = 1; i <= n; i++)scanf("%lld", &a[i]);t.build(1, 1, n);while (m--) {int op;scanf("%d", &op);if (op == 1) {int l, r;scanf("%d%d", &l, &r);printf("%lld\n", t.ask(1, l, r));} else if (op == 2) {long long k;int l, r;scanf("%d%d%lld", &l, &r, &k);t.change_mod(1, l, r, k);} else {int k;long long x;scanf("%d%lld", &k, &x);t.change_cover(1, k, x);}}return 0;
}