1. 程式人生 > 其它 >線段樹區間取模(The Child and Sequence)

線段樹區間取模(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;
}