LOJ #6029. 「雅礼集训 2017 Day1」市场 线段树维护区间除法

题目描述

从前有一个贸易市场,在一位执政官到来之前都是非常繁荣的,自从他来了之后,发布了一系列奇怪的政令,导致贸易市场的衰落。

有 \(n\) 个商贩,从\(0 \sim n - 1\) 编号,每个商贩的商品有一个价格\(a_i\),有两种政令:

\(l, r, c\),对于\(i \in [l, r], a_i \leftarrow a_i + c\)

\(l, r, d\),对于\(i \in [l, r], a_i \leftarrow \lfloor {a_i}/{d} \rfloor\)

现在有一个外乡的旅客想要了解贸易市场的信息,有两种询问方式:

给定 \(l, r\),求\(\min_{i \in [l, r]} a_i\)
给定 \(l, r\),求\(\sum_{i\in [l, r]} a_i\)

输入格式

第一行为两个空格隔开的整数 \(n, q\) 分别表示商贩个数和政令 \(+\) 询问个数。
第二行包含 \(n\) 个由空格隔开的整数\(a_0 \sim a_{n - 1}\)
接下来 \(q\) 行,每行表示一个操作,第一个数表示操作编号\(1 \sim 4\),接下来的输入和问题描述一致。

输出格式

对于每个 \(3\)、\(4\) 操作,输出询问答案。

样例

样例输入

10 10
-5 -4 -3 -2 -1 0 1 2 3 4
1 0 4 1
1 5 9 1
2 0 9 3
3 0 9
4 0 9
3 0 1
4 2 3
3 4 5
4 6 7
3 8 9

样例输出

-2
-2
-2
-2
0
1
1

数据范围与提示

对于 \(30\%\) 的数据,$n, q \leq 10 ^ 3 $;

对于 \(60\%\) 的数据,保证数据随机;

对于 \(100\%\) 的数据,\(1 \leq n, q \leq 10 ^ 5, 0 \leq l \leq r \leq n - 1, c \in [-10 ^ {4}, 10 ^ 4], d \in [2, 10 ^ 9]\)

分析

对于区间加、区间求和、区间求最小值的操作,像正常的线段树那样维护即可

对于区间除的操作,因为题目中要求向下取整,所以不能直接给整体除一个数

但是我们可以把除法转化为减法,把除以一个数变成减去一个数

这样,当一个区间内减去的数相同时,我们就可以给整体打一个标记

判断区间减去的数是否相同只需要判断区间最大值和区间最小值减去的值是否相同

如果不相同就一直下放,直到相同为止

这样的话我们再记录一个最大值就可以解决了

时间复杂度:\(O(可过)\)

代码

#include <cstdio>
#include <algorithm>
#include <cmath>
#define rg register
inline int read() {
    rg int x = 0, fh = 1;
    rg char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-')
            fh = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * fh;
}
const int maxn = 1e5 + 5;
int a[maxn], n, q;
struct trr {
    int l, r, laz, mmin, mmax, siz;
    long long sum;
} tr[maxn << 2];
void push_up(int da) {
    tr[da].mmax = std::max(tr[da << 1].mmax, tr[da << 1 | 1].mmax);
    tr[da].mmin = std::min(tr[da << 1].mmin, tr[da << 1 | 1].mmin);
    tr[da].sum = tr[da << 1].sum + tr[da << 1 | 1].sum;
}
void push_down(int da) {
    if (tr[da].laz) {
        tr[da << 1].laz += tr[da].laz;
        tr[da << 1 | 1].laz += tr[da].laz;
        tr[da << 1].sum += 1LL * tr[da << 1].siz * tr[da].laz;
        tr[da << 1 | 1].sum += 1LL * tr[da << 1 | 1].siz * tr[da].laz;
        tr[da << 1].mmax += tr[da].laz;
        tr[da << 1 | 1].mmax += tr[da].laz;
        tr[da << 1].mmin += tr[da].laz;
        tr[da << 1 | 1].mmin += tr[da].laz;
        tr[da].laz = 0;
    }
}
void build(int da, int l, int r) {
    tr[da].l = l, tr[da].r = r, tr[da].siz = r - l + 1;
    if (tr[da].l == tr[da].r) {
        tr[da].mmin = tr[da].mmax = a[l];
        tr[da].sum = a[l];
        return;
    }
    rg int mids = (tr[da].l + tr[da].r) >> 1;
    build(da << 1, l, mids);
    build(da << 1 | 1, mids + 1, r);
    push_up(da);
}
void ad(int da, int l, int r, int val) {
    if (tr[da].l >= l && tr[da].r <= r) {
        tr[da].laz += val;
        tr[da].sum += 1LL * val * tr[da].siz;
        tr[da].mmin += val;
        tr[da].mmax += val;
        return;
    }
    push_down(da);
    rg int mids = (tr[da].l + tr[da].r) >> 1;
    if (l <= mids)
        ad(da << 1, l, r, val);
    if (r > mids)
        ad(da << 1 | 1, l, r, val);
    push_up(da);
}
int cxmin(int da, int l, int r) {
    if (tr[da].l >= l && tr[da].r <= r) {
        return tr[da].mmin;
    }
    push_down(da);
    rg int mids = (tr[da].l + tr[da].r) >> 1, nans = 2147483647;
    if (l <= mids)
        nans = std::min(nans, cxmin(da << 1, l, r));
    if (r > mids)
        nans = std::min(nans, cxmin(da << 1 | 1, l, r));
    return nans;
}
long long cxsum(int da, int l, int r) {
    if (tr[da].l >= l && tr[da].r <= r) {
        return tr[da].sum;
    }
    push_down(da);
    rg int mids = (tr[da].l + tr[da].r) >> 1;
    rg long long nans = 0;
    if (l <= mids)
        nans += cxsum(da << 1, l, r);
    if (r > mids)
        nans += cxsum(da << 1 | 1, l, r);
    return nans;
}
void cf(int da, int l, int r, int val) {
    if (tr[da].l >= l && tr[da].r <= r) {
        rg int now1 = floor((double)tr[da].mmin / val) - tr[da].mmin;
        rg int now2 = floor((double)tr[da].mmax / val) - tr[da].mmax;
        if (tr[da].l == tr[da].r) {
            tr[da].mmin = floor((double)tr[da].mmin / val);
            tr[da].sum = tr[da].mmax = tr[da].mmin;
        } else if (now1 == now2) {
            tr[da].mmin += now1;
            tr[da].mmax += now1;
            tr[da].laz += now1;
            tr[da].sum += 1LL * tr[da].siz * now1;
        } else {
            push_down(da);
            cf(da << 1, l, r, val);
            cf(da << 1 | 1, l, r, val);
            push_up(da);
        }
        return;
    }
    push_down(da);
    rg int mids = (tr[da].l + tr[da].r) >> 1;
    if (l <= mids)
        cf(da << 1, l, r, val);
    if (r > mids)
        cf(da << 1 | 1, l, r, val);
    push_up(da);
}
int main() {
    n = read(), q = read();
    for (rg int i = 1; i <= n; i++) {
        a[i] = read();
    }
    build(1, 1, n);
    rg int aa, bb, cc, dd;
    for (rg int i = 1; i <= q; i++) {
        aa = read(), bb = read(), cc = read();
        bb++, cc++;
        if (aa == 1) {
            dd = read();
            ad(1, bb, cc, dd);
        } else if (aa == 2) {
            dd = read();
            cf(1, bb, cc, dd);
        } else if (aa == 3) {
            printf("%d\n", cxmin(1, bb, cc));
        } else {
            printf("%lld\n", cxsum(1, bb, cc));
        }
    }
    return 0;
}
上一篇:省选测试22


下一篇:LOJ#2076. 「JSOI2016」炸弹攻击(模拟退火)