2018 Multi-University Training Contest 2 1007 Naive Operations 线段树 区间更新区间查询

题目链接:1007 Naive Operations

题目大意

两个数组a和b, 长度为n,a一开始全都是0,b里面是1-n的排列
有两两种操作
add l, r : 将a数组[l, r]内所有元素+1
query l, r : 求 i = l r a i b i

思路

线段树区间查询区间内的最大值以及最大值的位置(如果有多个返回最左边的)
区间修改将区间内所有数字加上或减去一个数字

再使用另一个线段树或者树状数组保存实际答案

因为答案是向下取整的, 所以我们只有在a[i]增加到b[i]时答案的值才会增加1

一开始线段树中位置i的值是-b[i],更新时,将[l, r]区间每个元素+1,然后查询[l, r]最大值及其位置p,如果最大值为0,就说明有a[i]等于b[i]了, 那么将这个最大值的位置的值重新设置为-b[i], 将存储答案的线段树i位置+1,再查询[p, r]最大值,重复上面步骤

由于每次add平均最多会有一个点的答案+1,所以不会超时, 复杂度 O ( q l o g n )

因为没有lazy标记的线段树的模版, 而且很久没写过线段树了, 比赛的时候wa了好几发, 最后发现问题在pushDown上面, 这份代码就当作模版了

代码

1007 1045MS 5264K G++

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>

using namespace std;
const int maxn = 1e5 + 100, mod = 1e9 + 7, inf = 0x3f3f3f3f;
typedef pair<int, int> P;
typedef long long ll;

#define ls l, m, rt<<1
#define rs m+1, r, rt<<1|1
#define defm int m = (l+r)>>1

int b[maxn];
int q, n;
//线段树维护最左边的最大值
int mx[maxn << 2], mx_id[maxn << 2], col[maxn << 2];//最大值, 最大值下标, 懒惰标记

void pushUp(int rt)
{
    if (mx[rt << 1] >= mx[rt << 1 | 1])
    {
        mx[rt] = mx[rt << 1];
        mx_id[rt] = mx_id[rt << 1];
    }
    else
    {
        mx[rt] = mx[rt << 1 | 1];
        mx_id[rt] = mx_id[rt << 1 | 1];
    }
}

void pushDown(int rt)
{
    if (col[rt])
    {
        col[rt << 1] += col[rt];
        col[rt << 1 | 1] += col[rt];//这里是+=不是=
        mx[rt << 1] += col[rt];//这里+=col[rt]而不是col[rt<<1]
        mx[rt << 1 | 1] += col[rt];
        col[rt] = 0;
    }
}

void build(int l, int r, int rt)
{
    col[rt] = 0;
    if (l == r)
    {
        mx[rt] = -b[l];
        mx_id[rt] = l;
        return ;
    }
    int m = (l + r) >> 1;
    build(ls);
    build(rs);
    pushUp(rt);
}

void update(int L, int R, int c, int l, int r, int rt)
{
    if (L <= l && r <= R)
    {
        col[rt] += c;
        mx[rt] += c;
        return ;
    }
    pushDown(rt);
    int m = (l + r) >> 1;
    if (L <= m) update(L, R, c, ls);
    if (R > m) update(L, R, c, rs);
    pushUp(rt);
}

P query(int L, int R, int l, int r, int rt)//first最大值, second下标
{
    if (L <= l && r <= R)
    {
        return P(mx[rt], mx_id[rt]);
    }
    pushDown(rt);
    int m = (l + r) >> 1;
    P t1 = P(-inf, -inf), t2 = P(-inf, -inf);
    if (L <= m) t1 = query(L, R, ls);
    if (m < R) t2 = query(L, R, rs);
    if (t1.first >= t2.first) return t1;
    else return t2;
}

//保存答案的树状数组, 直接用模版了
ll bit[maxn];
inline int lowbit(int x) { return x & (-x); }
inline void init(int n) { memset(bit, 0, sizeof(int) * (n + 10)); }
ll sum(int k)
{
    return k <= 0 ? 0 : bit[k] + sum(k - lowbit(k));
}
void update(int p, int x)
{
    for ( ; p <= n; p += lowbit(p)) bit[p] += x;
}
inline ll query(int l, int r) { return sum(r) - sum(l - 1); }

int main()
{
    while (scanf("%d%d", &n, &q) == 2)
    {
        for (int i = 1; i <= n; ++i) scanf("%d", b + i);
        build(1, n, 1);
        init(n);
        char op[10];
        int l, r;
        for (int i = 0; i < q; ++i)
        {
            scanf("%s%d%d", op, &l, &r);
            if (op[0] == 'a')
            {
                update(l, r, 1, 1, n, 1);
                while (l <= r)
                {
                    P t = query(l , r, 1, n, 1);
                    if (t.first >= 0)//有a[i]等于0了,答案加1
                    {
                        update(t.second, 1);
                        update(t.second, t.second, -b[t.second], 1, n, 1);
                    }
                    else break;
                    l = t.second;
                }
            }
            else
            {
                printf("%lld\n", query(l, r));
            }
        }
    }


    return 0;
}

猜你喜欢

转载自blog.csdn.net/litmxs/article/details/81206937