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=L∑Rai
考虑到操作只有对区间每个数做除法,以及区间赋值。所以我们为了精简写法,将除法操作用赋值的方式来实现。
我们对线段树的每一个节点维护区间和、赋值标记、最大值、最小值。在进行除法的时候,判断最大值与最小值分别除以 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=l∑rai
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=l∑rai
(本题中两个操作的
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=l∑r⌊biai⌋
利用特点: 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=l∑rai
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 1≤i≤106时 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=l∑rai
因为有第一种操作,即区间加减操作,所以不同于上面你的除法,我们这道题将除法操作变成加减法。
设区间最大值 x x x,最小值 y y y,令 d 1 = x − ⌊ x / d ⌋ d_1=x-\lfloor {x/d}\rfloor d1=x−⌊x/d⌋, d 2 = y − ⌊ y / d ⌋ d_2=y-\lfloor {y/d}\rfloor d2=y−⌊y/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;
}