线段树区间取模(The Child and Sequence)

题面

The Child and Sequence

题解

区间和和单点修改是我们熟悉的。
但是对于第二种区间取模操作,我们不难发现,如果按照类似于区间加,维护一个懒标记的话,是很难维护的,因为它很不好合并。
如果做过花神游历各国,可以类比一下区间开方的操作,暴力修改。
但是区间开方开个几次就变成 \(0\)\(1\) 了,区间取模是否具有类似的性质呢?
对于数 \(x\), \(x = x \pmod p(x < p)\), \(x < \frac{x}{2} \pmod p(x > p)\)
对于式一显然成立。
对于式二,当 \(p < \frac{x}{2}\) 时,\(x \bmod p < p < \frac{x}{2}\),当 \(p > \frac{x}{2}\) 时,\(x \bmod p = x - p < \frac{x}{2}\)
所以我们维护区间最大值,当最大值小于模数时,直接返回,当最大值大于模数时,暴力修改,因为每次区间最大值减半,所以暴力修改不超过 \(log\) 次,所以复杂度是有保障的。

代码

#include<cstdio>
#include<iostream>

using namespace std;

typedef long long LL;

const int N = 1e5 + 5;

int n, m, a[N];

struct SegmentTree {
	#define M N << 2
	int l[M], r[M], Max[M]; LL sum[M];
	inline void pushup(int p) {
		sum[p] = sum[p << 1] + sum[p << 1 | 1];
		Max[p] = max(Max[p << 1], Max[p << 1 | 1]);
	}
	void build(int p, int L, int R) {
		l[p] = L, r[p] = R;
		if(L == R) {
			sum[p] = Max[p] = a[L];
			return ;
		}
		int mid = (L + R) >> 1;
		build(p << 1, L, mid);
		build(p << 1 | 1, mid + 1, R);
		pushup(p);
	}
	void update(int p, int pos, int k) {
		if(l[p] == r[p]) {
			sum[p] = Max[p] = k;
			return ;
		}
		int mid = (l[p] + r[p]) >> 1;
		if(pos <= mid) update(p << 1, pos, k);
		else update(p << 1 | 1, pos, k);
		pushup(p);
	}
	void Mod(int p, int L, int R, int mod) {
		if(Max[p] < mod) return ;
		if(l[p] == r[p]) {
			sum[p] %= mod, Max[p] %= mod;
			return ;
		}
		int mid = (l[p] + r[p]) >> 1;
		if(L <= mid) Mod(p << 1, L, R, mod);
		if(R >  mid) Mod(p << 1 | 1, L, R, mod);
		pushup(p);
	}
	LL query(int p, int L, int R) {
		if(L <= l[p] && r[p] <= R) return sum[p];
		int mid = (l[p] + r[p]) >> 1; LL ans = 0;
		if(L <= mid) ans += query(p << 1, L, R);
		if(R >  mid) ans += query(p << 1 | 1, L, R);
		return ans;
	} 
}tr;

int main() {
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
	tr.build(1, 1, n);
	for(int i = 1, opr, x, y, mod; i <= m; i++) {
		scanf("%d%d%d", &opr, &x, &y);
		if(opr == 1) printf("%lld\n", tr.query(1, x, y));
		else if(opr == 2) {
			scanf("%d", &mod);
			tr.Mod(1, x, y, mod);
		}
		else tr.update(1, x, y);
	}
	return 0;
}
posted @ 2021-07-28 21:46  init-神眷の樱花  阅读(380)  评论(0)    收藏  举报