线段树板子题。
难点在于操作 4。把 \(\lfloor\frac a d \rfloor\) 转化为 \(a - s\),其中 \(s = \lfloor\frac a d \rfloor - a\)。那么切割区间直到其中所有数对应的 \(s\) 都一样时同时减去 \(s\)。
#include <cmath>
#include <cstdio>
#include <algorithm>
typedef long long ll;
const int MAXN = 1e5 + 19;
class Segment{
private:
struct Node{
int l, r;
int min, max;
ll sum;
int tag;
}tr[MAXN << 2];
void push_up(int node){
tr[node].min = std::min(tr[node << 1].min, tr[node << 1 | 1].min);
tr[node].max = std::max(tr[node << 1].max, tr[node << 1 | 1].max);
tr[node].sum = tr[node << 1].sum + tr[node << 1 | 1].sum;
}
void push_down(int node){
if(tr[node].tag){
tr[node << 1].min += tr[node].tag;
tr[node << 1].max += tr[node].tag;
tr[node << 1].sum += (ll)tr[node].tag * (tr[node << 1].r - tr[node << 1].l + 1);
tr[node << 1].tag += tr[node].tag;
tr[node << 1 | 1].min += tr[node].tag;
tr[node << 1 | 1].max += tr[node].tag;
tr[node << 1 | 1].sum += (ll)tr[node].tag * (tr[node << 1 | 1].r - tr[node << 1 | 1].l + 1);
tr[node << 1 | 1].tag += tr[node].tag;
tr[node].tag = 0;
}
}
void build(int node, int l, int r, int *w){
tr[node].l = l, tr[node].r = r;
if(l == r){
tr[node].min = w[l];
tr[node].max = w[l];
tr[node].sum = w[l];
tr[node].tag = 0;
return;
}
int mid = (l + r) >> 1;
build(node << 1, l, mid, w);
build(node << 1 | 1, mid + 1, r, w);
push_up(node);
}
int queryMin(int node, int l, int r){
if(tr[node].l >= l && tr[node].r <= r)
return tr[node].min;
push_down(node);
int mid = (tr[node].l + tr[node].r) >> 1,
res = 0x7fffffff;
if(l <= mid)
res = std::min(res, queryMin(node << 1, l, r));
if(r > mid)
res = std::min(res, queryMin(node << 1 | 1, l, r));
return res;
}
ll querySum(int node, int l, int r){
if(tr[node].l >= l && tr[node].r <= r)
return tr[node].sum;
push_down(node);
int mid = (tr[node].l + tr[node].r) >> 1;
ll res = 0;
if(l <= mid)
res += querySum(node << 1, l, r);
if(r > mid)
res += querySum(node << 1 | 1, l, r);
return res;
}
void add(int node, int l, int r, const int &val){
if(tr[node].l >= l && tr[node].r <= r){
tr[node].min += val;
tr[node].max += val;
tr[node].sum += (ll)val * (tr[node].r - tr[node].l + 1);
tr[node].tag += val;
return;
}
push_down(node);
int mid = (tr[node].l + tr[node].r) >> 1;
if(l <= mid)
add(node << 1, l, r, val);
if(r > mid)
add(node << 1 | 1, l, r, val);
push_up(node);
}
int div(int a, int b){
return (int)std::floor((double)a / (double)b);
}
void subdiv(int node, const int &val){
if(tr[node].max - div(tr[node].max, val) == tr[node].min - div(tr[node].min, val)){
int m = div(tr[node].max, val) - tr[node].max;
tr[node].min += m;
tr[node].max += m;
tr[node].sum += (ll)m * (tr[node].r - tr[node].l + 1);
tr[node].tag += m;
return;
}
push_down(node);
subdiv(node << 1, val);
subdiv(node << 1 | 1, val);
push_up(node);
}
void div(int node, int l, int r, const int &val){
if(tr[node].l >= l && tr[node].r <= r){
subdiv(node, val);
return;
}
push_down(node);
int mid = (tr[node].l + tr[node].r) >> 1;
if(l <= mid)
div(node << 1, l, r, val);
if(r > mid)
div(node << 1 | 1, l, r, val);
push_up(node);
}
public:
void build(int l, int r, int *w){
build(1, l, r, w);
}
int queryMin(int l, int r){
return queryMin(1, l, r);
}
ll querySum(int l, int r){
return querySum(1, l, r);
}
void add(int l, int r, const int &val){
add(1, l, r, val);
}
void div(int l, int r, const int &val){
div(1, l, r, val);
}
}myTree;
int n, q;
int a[MAXN];
int main(){
std::scanf("%d%d", &n, &q);
for(int i = 1; i <= n; ++i)
std::scanf("%d", a + i);
myTree.build(1, n, a);
while(q--){
int opt, l, r, c, d;
std::scanf("%d", &opt);
if(opt == 1){
std::scanf("%d%d%d", &l, &r, &c);
++l, ++r;
myTree.add(l, r, c);
}
else if(opt == 2){
std::scanf("%d%d%d", &l, &r, &d);
++l, ++r;
myTree.div(l, r, d);
}
else if(opt == 3){
std::scanf("%d%d", &l, &r);
++l, ++r;
std::printf("%d\n", myTree.queryMin(l, r));
}
else if(opt == 4){
std::scanf("%d%d", &l, &r);
++l, ++r;
std::printf("%lld\n", myTree.querySum(l, r));
}
}
return 0;
}