【题型总结】势能线段树

吉老师势能线段树论文

ABC256Ex - I like Query Problem

题目链接

1 L R x:对 i = L , L + 1 , ⋯   , R i=L,L+1,\cdots,R i=L,L+1,,R,将 a i a_i ai的值更新为 ⌊ a i x ⌋ \lfloor \frac{a_i}{x} \rfloor xai
2 L R y:对 i = L , L + 1 , ⋯   , R i=L,L+1,\cdots,R i=L,L+1,,R,将 a i a_i ai的值更新为 y y y
3 L R:输出 ∑ i = L R a i \displaystyle\sum\limits_{i=L}^R a_i i=LRai

考虑到操作只有对区间每个数做除法,以及区间赋值。所以我们为了精简写法,将除法操作用赋值的方式来实现。

我们对线段树的每一个节点维护区间和、赋值标记、最大值、最小值。在进行除法的时候,判断最大值与最小值分别除以 x x x,看看两者结果是否相同,如果相同,则说明这个区间内的所有数值在除以 x x x后的结果都是相同的,那么就对这个区间进行区间赋值。否则,递归往孩子结点去寻找。其他的和普通线段树是一样的操作。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;

int n, q;
int a[500005];

struct Tree {
	int l, r;
	LL sum, mn, mx, chg;
}t[2000005];

void pu(int ni) {
	t[ni].sum = t[ni << 1].sum + t[ni << 1 | 1].sum;
	t[ni].mn = min(t[ni << 1].mn, t[ni << 1 | 1].mn);
	t[ni].mx = max(t[ni << 1].mx, t[ni << 1 | 1].mx);
	t[ni].chg = -1;
}

void pd(int ni) {
	int li = (ni << 1), ri = (ni << 1 | 1);
	int ls = t[li].r - t[li].l + 1, rs = t[ri].r - t[ri].l + 1;
	if (t[ni].chg >= 0) {
		t[li].sum = t[ni].chg * ls; t[li].chg = t[ni].chg;
		t[ri].sum = t[ni].chg * rs; t[ri].chg = t[ni].chg;
		t[li].mx = t[li].mn = t[ri].mx = t[ri].mn = t[ni].chg;
		t[ni].chg = -1;
	}
}

void build_tree(int ni, int l, int r) {
	t[ni].l = l; t[ni].r = r;
	if (l == r) {
		t[ni].sum = t[ni].mn = t[ni].mx = a[l];
		t[ni].chg = -1;
		return;
	}
	int mid = (l + r) >> 1;
	build_tree(ni << 1, l, mid);
	build_tree(ni << 1 | 1, mid + 1, r);
	pu(ni);
}

void div(int ni, int l, int r, LL x) {
	if (l <= t[ni].l and t[ni].r <= r) {
		LL res1 = t[ni].mx / x, res2 = t[ni].mn / x;
		if (res1 == res2) {
			t[ni].chg = t[ni].mx = t[ni].mn = res1;
			t[ni].sum = res1 * (t[ni].r - t[ni].l + 1);
			return;
		}
	}
	int mid = (t[ni].l + t[ni].r) >> 1;
	pd(ni);
	if (l <= mid) div(ni << 1, l, r, x);
	if (mid < r) div(ni << 1 | 1, l, r, x);
	pu(ni);
}

void fix(int ni, int l, int r, LL x) {
	if (l <= t[ni].l and t[ni].r <= r) {
		t[ni].chg = x;
		t[ni].sum = x * (t[ni].r - t[ni].l + 1);
		t[ni].mx = t[ni].mn = x;
		return;
	}
	int mid = (t[ni].l + t[ni].r) >> 1;
	pd(ni);
	if (l <= mid) fix(ni << 1, l, r, x);
	if (mid < r) fix(ni << 1 | 1, l, r, x);
	pu(ni);
}

LL query(int ni, int l, int r) {
	if (l <= t[ni].l and t[ni].r <= r) {
		return t[ni].sum;
	}
	int mid = (t[ni].l + t[ni].r) >> 1;
	pd(ni);
	LL ans1 = 0, ans2 = 0;
	if (l <= mid) ans1 = query(ni << 1, l, r);
	if (mid < r) ans2 = query(ni << 1 | 1, l, r);
	return ans1 + ans2;
}

void main2() {
	cin >> n >> q;
	for (int i = 1; i <= n; ++i) {
		cin >> a[i];
	}
	build_tree(1, 1, n);
	for (int i = 1; i <= q; ++i) {
		int o, l, r, x; cin >> o;
		if (o == 1) {
			cin >> l >> r >> x;
			div(1, l, r, x);
		}
		else if (o == 2) {
			cin >> l >> r >> x;
			fix(1, l, r, x);
		}
		else {
			cin >> l >> r;
			cout << query(1, l, r) << '\n';
		}
	}
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	LL _;
//	cin >> _;
	_ = 1;
	while (_--) main2();
	return 0;
}

CF438D - The Child and Sequence

题目链接

1 l r:输出 ∑ i = l r a i \displaystyle\sum\limits_{i=l}^r a_i i=lrai
2 l r x:对 i = L , L + 1 , ⋯   , R i=L,L+1,\cdots,R i=L,L+1,,R,将 a i a_i ai的值更新为 a i   m o d   x a_i \bmod x aimodx
3 k x:将 a k a_k ak的值更新为 x x x

考虑特性:当 a i < x a_i<x ai<x时, a i a_i ai x x x取模就是他自己。

可以引申出:对一个区间来说,如果这个区间的最大值小于 x x x,那么对于这个区间就不需要执行第二个操作而直接返回了。如果不满足这个条件的话,就继续向下递归,要么满足上面条件之后直接返回,要么当区间长度为 1 1 1时,直接修改值。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;

struct Tree {
	int l, r;
	LL sum, tag, mx;
}t[500005];

int n, m;
LL a[100005];

void pu(int ni) {
	t[ni].sum = t[ni << 1].sum + t[ni << 1 | 1].sum;
	t[ni].mx = max(t[ni << 1].mx, t[ni << 1 | 1].mx);
	t[ni].tag = -1; 
}

void pd(int ni) {
	if (t[ni].tag < 0) return;
	int li = (ni << 1), ri = (ni << 1 | 1);
	int ls = t[li].r - t[li].l + 1, rs = t[ri].r - t[ri].l + 1;
	t[li].sum = t[ni].tag * ls; t[ri].sum = t[ni].tag * rs;
	t[li].tag = t[ri].tag = t[li].mx = t[ri].tag = t[ni].tag;
	t[ni].tag = -1;
}

void build_tree(int ni, int l, int r) {
	t[ni].l = l; t[ni].r = r;
	if (l == r) {
		t[ni].sum = t[ni].mx = a[l];
		t[ni].tag = -1;
		return;
	}
	int mid = (l + r) >> 1;
	build_tree(ni << 1, l, mid);
	build_tree(ni << 1 | 1, mid + 1, r);
	pu(ni);
}

void opt(int ni, int l, int r, LL x) {
	if (l <= t[ni].l and t[ni].r <= r) {
		if (t[ni].mx < x) {
			return;
		}
		if (t[ni].l == t[ni].r) {
			t[ni].sum = t[ni].mx = t[ni].sum % x;
			t[ni].tag = -1;
			return;
		}
	}
	int mid = (t[ni].l + t[ni].r) >> 1;
	pd(ni);
	if (l <= mid) opt(ni << 1, l, r, x);
	if (mid < r) opt(ni << 1 | 1, l, r, x);
	pu(ni);
}

void fix(int ni, int l, LL x) {
	if (l <= t[ni].l and t[ni].r <= l) {
		t[ni].tag = t[ni].sum = t[ni].mx = x;
		return;
	}
	int mid = (t[ni].l + t[ni].r) >> 1;
	pd(ni);
	if (l <= mid) fix(ni << 1, l, x);
	else fix(ni << 1 | 1, l, x);
	pu(ni); 
}

LL query(int ni, int l, int r) {
	if (l <= t[ni].l and t[ni].r <= r) {
		return t[ni].sum;
	}
	int mid = (t[ni].l + t[ni].r) >> 1;
	pd(ni);
	LL ans1 = 0, ans2 = 0;
	if (l <= mid) ans1 = query(ni << 1, l, r);
	if (mid < r) ans2 = query(ni << 1 | 1, l, r);
	return ans1 + ans2;
}

void main2() {
	cin >> n >> m;
	for (int i = 1; i <= n; ++i) {
		cin >> a[i];
	}
	build_tree(1, 1, n);
	for (int i = 1; i <= m; ++i) {
		int o; LL l, r, x;
		cin >> o;
		if (o == 1) {
			cin >> l >> r;
			cout << query(1, l, r) << '\n';
		}
		else if (o == 2) {
			cin >> l >> r >> x;
			opt(1, l, r, x);
		}
		else {
			cin >> l >> x;
			fix(1, l, x);
		}
	}
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	LL _;
//	cin >> _;
	_ = 1;
	while (_--) main2();
	return 0;
}

LuoguP4145 - 上帝造题的七分钟 2 / 花神游历各国

题目链接

0 l r:对 i = L , L + 1 , ⋯   , R i=L,L+1,\cdots,R i=L,L+1,,R,将 a i a_i ai的值更新为 ⌊ a i ⌋ \lfloor \sqrt {a_i} \rfloor ai
1 l r:输出 ∑ i = l r a i \displaystyle\sum\limits_{i=l}^r a_i i=lrai
(本题中两个操作的 l l l r r r可能会出现 l > r l>r l>r,需要特判调换位置)

注意到只有当区间内的元素最大值小于等于 1 1 1时,这个区间不需要操作,直接返回。其他情况单点修改。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;

int n, m;
LL a[100005];

struct Tree {
	int l, r;
	LL sum, mx;
}t[500005];

void pu(int ni) {
	t[ni].sum = t[ni << 1].sum + t[ni << 1 | 1].sum;
	t[ni].mx = max(t[ni << 1].mx, t[ni << 1 | 1].mx);
}

void build_tree(int ni, int l, int r) {
	t[ni].l = l; t[ni].r = r;
	if (l == r) {
		t[ni].sum = t[ni].mx = a[l];
		return;
	}
	int mid = (l + r) >> 1;
	build_tree(ni << 1, l, mid);
	build_tree(ni << 1 | 1, mid + 1, r);
	pu(ni); 
}

void opt(int ni, int l, int r) {
	if (l <= t[ni].l and t[ni].r <= r) {
		if (t[ni].mx <= 1) {
			return;
		}
		if (t[ni].l == t[ni].r) {
			t[ni].sum = t[ni].mx = sqrt(t[ni].sum);
			return;
		}
	}
	int mid = (t[ni].l + t[ni].r) >> 1;
	if (l <= mid) opt(ni << 1, l, r);
	if (mid < r) opt(ni << 1 | 1, l, r);
	pu(ni);
}

LL query(int ni, int l, int r) {
	if (l <= t[ni].l and t[ni].r <= r) {
		return t[ni].sum;
	}
	int mid = (t[ni].l + t[ni].r) >> 1;
	LL ans1 = 0, ans2 = 0;
	if (l <= mid) ans1 = query(ni << 1, l, r);
	if (mid < r) ans2 = query(ni << 1 | 1, l, r);
	return ans1 + ans2;
}

void main2() {
	cin >> n;
	for (int i = 1; i <= n; ++i) {
		cin >> a[i];
	}
	cin >> m;
	build_tree(1, 1, n);
	for (int i = 1; i <= m; ++i) {
		int o; cin >> o;
		LL l, r;
		if (o == 0) {
			cin >> l >> r;
			if (l > r) swap(l, r);
			opt(1, l, r);
		}
		else {
			cin >> l >> r;
			if (l > r) swap(l, r);
			cout << query(1, l, r) << '\n';
		}
	}
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	LL _;
//	cin >> _;
	_ = 1;
	while (_--) main2();
	return 0;
}

HDU6315 - Naive Operations

题目链接

给定序列长度 n n n,给定序列 b b b(是 n n n的一个排列)
add l r:对 i = L , L + 1 , ⋯   , R i=L,L+1,\cdots,R i=L,L+1,,R,将 a i a_i ai的值更新为 a i + 1 a_i + 1 ai+1
query l r:输出 ∑ i = l r ⌊ a i b i ⌋ \displaystyle\sum\limits_{i=l}^r \lfloor \frac{a_i}{b_i} \rfloor i=lrbiai

利用特点: a i a_i ai进行add操作 b i b_i bi次,才会在query操作中产生 1 1 1的贡献,所以维护区间内上限的最小值(初始上限为 b i b_i bi,维护 a i a_i ai的最大值,如果区间内 + 1 +1 +1后,区间最大的 a i a_i ai仍小于区间最小上限,只需要更新 t a g tag tag,因为本次更新,这个区间内的 ⌊ a i b i ⌋ \lfloor \frac{a_i}{b_i} \rfloor biai没有变化。否则,向下递归更新。如果当前加的数超过了 b i b_i bi,那么就让这个数的答案 + 1 +1 +1,同时将上限上调 b i b_i bi。比如说,设区间上限是 x x x,初始 a i = 3 , x = b i = 4 a_i=3,x=b_i=4 ai=3,x=bi=4,当执行add操作后,变成了 a i = x = 4 a_i=x=4 ai=x=4,这个时候这个数的 ⌊ a i b i ⌋ \lfloor \frac{a_i}{b_i} \rfloor biai + 1 +1 +1。将区间答案 + 1 +1 +1更新后,上调上限,于是对于这个区间就变成了 a i = 4 , x = 8 a_i=4,x=8 ai=4,x=8

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;

int n, q;
int b[100005];

struct Tree {
	int l, r;
	int mx, bx, sum, tag;
}t[500005];

void pu(int ni) {
	t[ni].sum = t[ni << 1].sum + t[ni << 1 | 1].sum;
	t[ni].mx = max(t[ni << 1].mx, t[ni << 1 | 1].mx);
	t[ni].bx = min(t[ni << 1].bx, t[ni << 1 | 1].bx);
}

void pd(int ni) {
	if (!t[ni].tag) return;
	t[ni << 1].tag += t[ni].tag;
	t[ni << 1].mx += t[ni].tag;
	t[ni << 1 | 1].tag += t[ni].tag;
	t[ni << 1 | 1].mx += t[ni].tag;
	t[ni].tag = 0;
}

void build_tree(int ni, int l, int r) {
	t[ni].l = l; t[ni].r = r; t[ni].tag = 0;
	if (l == r) {
		t[ni].bx = b[l];
		t[ni].sum = t[ni].mx = 0;
		return;
	}
	int mid = (l + r) >> 1;
	build_tree(ni << 1, l, mid);
	build_tree(ni << 1 | 1, mid + 1, r);
	pu(ni);
}

void add(int ni, int l, int r) {
	if (l <= t[ni].l and t[ni].r <= r) {
		++t[ni].mx;
		if (t[ni].mx < t[ni].bx) {
			++t[ni].tag; return;
		}
		if (t[ni].l == t[ni].r) {
			LL d = (t[ni].mx - t[ni].bx) / b[t[ni].l] + 1;
			t[ni].bx += (d * b[t[ni].l]);
			t[ni].sum += d;
			return;
		}
	}
	int mid = (t[ni].l + t[ni].r) >> 1;
	pd(ni);
	if (l <= mid) add(ni << 1, l, r);
	if (mid < r) add(ni << 1 | 1, l, r);
	pu(ni);
}

int query(int ni, int l, int r) {
	if (l <= t[ni].l and t[ni].r <= r) {
		return t[ni].sum;
	}
	int mid = (t[ni].l + t[ni].r) >> 1;
	pd(ni);
	int ans = 0;
	if (l <= mid) ans += query(ni << 1, l, r);
	if (mid < r) ans += query(ni << 1 | 1, l, r);
	pu(ni);
	return ans;
}

void main2() {
	for (int i = 1; i <= n; ++i) {
		cin >> b[i];
	}
	build_tree(1, 1, n);
	for (int i = 1; i <= q; ++i) {
		string x;
		int l, r;
		cin >> x >> l >> r;
		if (x[0] == 'a') {
			add(1, l, r);
		}
		else {
			cout << query(1, l, r) << '\n';
		}
	}
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	LL _;
//	cin >> _;
	_ = 1;
	while (cin >> n >> q) main2();
	return 0;
}

CF920F - SUM and REPLACE

题目链接

1 l r:对 i = L , L + 1 , ⋯   , R i=L,L+1,\cdots,R i=L,L+1,,R,将 a i a_i ai的值更新为 D ( a i ) D(a_i) D(ai)
2 l r:输出 ∑ i = l r a i \displaystyle\sum\limits_{i=l}^r a_i i=lrai

D ( a i ) D(a_i) D(ai) a i a_i ai的因子个数,如 D ( 2 ) = 2 , D ( 6 ) = 4 D(2)=2,D(6)=4 D(2)=2,D(6)=4,因为 2 2 2的因子有 1 , 2 1,2 1,2 6 6 6的因子有 1 , 2 , 3 , 6 1,2,3,6 1,2,3,6

我们可以用类似素数筛的方法在 O ( n log ⁡ n ) O(n\log n) O(nlogn)的时间复杂度内完成 1 ≤ i ≤ 1 0 6 1\leq i\leq 10^6 1i106 D ( i ) D(i) D(i)的值的求解。我们发现,当 i < 3 i<3 i<3时, i = D ( i ) i=D(i) i=D(i),所以当区间内的最大值小于 3 3 3时,可以直接返回。其他情况下向下递归区间直到区间长度为 1 1 1,暴力修改这些区间的值。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;

int n, m;
int a[300005], d[1000005];

void init() {
	for (int i = 1; i <= 1000000; ++i) {
		d[i] = 0;
	}
	for (int i = 1; i <= 1000000; ++i) {
		for (int j = i; j <= 1000000; j += i) {
			++d[j];
		}
	}
}

struct Tree {
	int l, r;
	LL sum, mx;
}t[1500005];

void pu(int ni) {
	t[ni].sum = t[ni << 1].sum + t[ni << 1 | 1].sum;
	t[ni].mx = max(t[ni << 1].mx, t[ni << 1 | 1].mx); 
}

void build_tree(int ni, int l, int r) {
	t[ni].l = l; t[ni].r = r;
	if (l == r) {
		t[ni].sum = t[ni].mx = a[l];
		return;
	}
	int mid = (l + r) >> 1;
	build_tree(ni << 1, l, mid);
	build_tree(ni << 1 | 1, mid + 1, r);
	pu(ni);
}

void fix(int ni, int l, int r) {
	if (l <= t[ni].l and t[ni].r <= r) {
		if (t[ni].mx < 3) {
			return;
		}
		if (t[ni].l == t[ni].r) {
			LL nxt = d[t[ni].sum];
			t[ni].sum = t[ni].mx = nxt;
			return;
		}
	}
	int mid = (t[ni].l + t[ni].r) >> 1;
	if (l <= mid) fix(ni << 1, l, r);
	if (mid < r) fix(ni << 1 | 1, l, r);
	pu(ni); 
}

LL query(int ni, int l, int r) {
	if (l <= t[ni].l and t[ni].r <= r) {
		return t[ni].sum;
	}
	int mid = (t[ni].l + t[ni].r) >> 1;
	LL res = 0;
	if (l <= mid) res += query(ni << 1, l, r);
	if (mid < r) res += query(ni << 1 | 1, l, r);
	return res;
}

void main2() {
	cin >> n >> m;
	for (int i = 1; i <= n; ++i) {
		cin >> a[i];
	}
	build_tree(1, 1, n);
	for (int i = 1; i <= m; ++i) {
		int o, l, r;
		cin >> o >> l >> r;
		if (o == 1) {
			fix(1, l, r);
		}
		else {
			cout << query(1, l, r) << '\n';
		}
	}
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	init();
	LL _;
//	cin >> _;
	_ = 1;
	while (_--) main2();
	return 0;
}

LOJ6029 - 「雅礼集训 2017 Day1」市场

题目链接

1 l r c:对 i = L , L + 1 , ⋯   , R i=L,L+1,\cdots,R i=L,L+1,,R,将 a i a_i ai的值更新为 a i + c a_i+c ai+c
2 l r d:对 i = L , L + 1 , ⋯   , R i=L,L+1,\cdots,R i=L,L+1,,R,将 a i a_i ai的值更新为 ⌊ a i / d ⌋ \lfloor {a_i/d}\rfloor ai/d
3 l r:输出 min ⁡ i ∈ [ l , r ] a i \min _{i∈[l,r]} a_i mini[l,r]ai
4 l r:输出 ∑ i = l r a i \displaystyle\sum\limits_{i=l}^r a_i i=lrai

因为有第一种操作,即区间加减操作,所以不同于上面你的除法,我们这道题将除法操作变成加减法。

设区间最大值 x x x,最小值 y y y,令 d 1 = x − ⌊ x / d ⌋ d_1=x-\lfloor {x/d}\rfloor d1=xx/d d 2 = y − ⌊ y / d ⌋ d_2=y-\lfloor {y/d}\rfloor d2=yy/d。如果 d 1 = d 2 d_1=d_2 d1=d2,那么直接对这个区间进行区间减 d 1 d_1 d1的操作,然后直接从这个区间返回。否则向下递归,直到区间长度为 1 1 1,暴力更新。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;

int n, q;
int a[100005];

struct Tree {
	int l, r; LL sum, mx, mn, tag;
}t[1000005];

void pu(int ni) {
	t[ni].sum = t[ni << 1].sum + t[ni << 1 | 1].sum;
	t[ni].mx = max(t[ni << 1].mx, t[ni << 1 | 1].mx);
	t[ni].mn = min(t[ni << 1].mn, t[ni << 1 | 1].mn);
}

void pd(int ni) {
	int li = (ni << 1), ri = (ni << 1 | 1);
	int ls = t[li].r - t[li].l + 1, rs = t[ri].r - t[ri].l + 1;
	t[li].tag += t[ni].tag;
	t[ri].tag += t[ni].tag;
	t[li].sum += (t[ni].tag * ls); t[ri].sum += (t[ni].tag * rs);
	t[li].mx += t[ni].tag; t[ri].mx += t[ni].tag;
	t[li].mn += t[ni].tag; t[ri].mn += t[ni].tag;
	t[ni].tag = 0;
}

void build_tree(int ni, int l, int r) {
	t[ni].l = l; t[ni].r = r;
	t[ni].tag = 0;
	if (l == r) {
		t[ni].sum = t[ni].mn = t[ni].mx = a[l];
		return;
	}
	int mid = (l + r) >> 1;
	build_tree(ni << 1, l, mid);
	build_tree(ni << 1 | 1, mid + 1, r);
	pu(ni);
}

void add(int ni, int l, int r, LL x) {
	if (l <= t[ni].l and t[ni].r <= r) {
		t[ni].tag += x;
		t[ni].mn += x; t[ni].mx += x;
		t[ni].sum += (x * (t[ni].r - t[ni].l + 1));
		return;
	}
	int mid = (t[ni].l + t[ni].r) >> 1;
	pd(ni);
	if (l <= mid) add(ni << 1, l, r, x);
	if (mid < r) add(ni << 1 | 1, l, r, x);
	pu(ni);
}

LL d(LL x, LL y) {
	if (x >= 0) return (abs(x) / y);
	else {
		if (abs(x) % y == 0) return -(abs(x) / y);
		else return -(abs(x) / y + 1);
	}
}

void fix(int ni, int l, int r, LL x) {
	if (l <= t[ni].l and t[ni].r <= r) {
		LL res1 = t[ni].mx - d(t[ni].mx, x);
		LL res2 = t[ni].mn - d(t[ni].mn, x);
		if (res1 == res2) {
			t[ni].tag -= res1;
			t[ni].mn -= res1; t[ni].mx -= res1;
			t[ni].sum -= (res1 * (t[ni].r - t[ni].l + 1));
			return;
		}
		if (t[ni].l == t[ni].r) {
			LL nxt = d(t[ni].sum, x);
			t[ni].sum = t[ni].mx = t[ni].mn = nxt;
			return;
		}
	}
	int mid = (t[ni].l + t[ni].r) >> 1;
	pd(ni);
	if (l <= mid) fix(ni << 1, l, r, x);
	if (mid < r) fix(ni << 1 | 1, l, r, x);
	pu(ni);
}

LL query(int ni, int l, int r) {
	if (l <= t[ni].l and t[ni].r <= r) {
		return t[ni].sum;
	}
	int mid = (t[ni].l + t[ni].r) >> 1;
	pd(ni);
	LL res = 0;
	if (l <= mid) res += query(ni << 1, l, r);
	if (mid < r) res += query(ni << 1 | 1, l, r);
	pu(ni);
	return res;
}

LL qmin(int ni, int l, int r) {
	if (l <= t[ni].l and t[ni].r <= r) {
		return t[ni].mn;
	}
	int mid = (t[ni].l + t[ni].r) >> 1;
	pd(ni);
	LL res = 1e17;
	if (l <= mid) res = min(res, qmin(ni << 1, l, r));
	if (mid < r) res = min(res, qmin(ni << 1 | 1, l, r));
	pu(ni);
	return res;
}

void main2() {
	cin >> n >> q;
	for (int i = 1; i <= n; ++i) {
		cin >> a[i];
	}
	build_tree(1, 1, n);
	for (int i = 1; i <= q; ++i) {
		int o, l, r, x;
		cin >> o >> l >> r;
		++l; ++r;
		if (o == 1) {
			cin >> x;
			add(1, l, r, x);
		}
		else if (o == 2) {
			cin >> x;
			fix(1, l, r, x);
		}
		else if (o == 3) {
			cout << qmin(1, l, r) << '\n';
		}
		else cout << query(1, l, r) << '\n';
	}
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	LL _;
//	cin >> _;
	_ = 1;
	while (_--) main2();
	return 0;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值