The Child and Sequence 题解

题目链接

题目描述

有一个长度为 \(n\) 的数列 \(\{a_n\}\)\(m\) 次操作,操作内容如下:

  1. 格式为 1 l r,表示求 \(\sum \limits _{i=l}^{r} a_i\) 的值并输出。
  2. 格式为 2 l r x,表示对区间 \([l,r]\) 内每个数取模,模数为 \(x\)
  3. 格式为 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 这题,如果实在做不出来 那你线段树学了个啥 可以去参考一下题解
  • 对于操作 \(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 \mathbb{N}_+ , kp \in \mathbb{N}_+\) ,所以 \(k \in \mathbb{N}_+\) ,所以 \(k \geq 1\)

      然后我们就得出以下式子:

\[c < kp \]

\[c + c < kp + c \]

\[2c < kp + c \]

\[2c < x \]

\[c < \frac{x}{2} \]

\[x\ \text{mod}\ p < \frac{x}{2}(x\geq p) \]


代码:

#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;
}
posted @ 2025-01-22 19:06  ZaleClover  阅读(22)  评论(0)    收藏  举报