2018 Multi-University Training Contest 2-1007(hdu 6315)-题解

一、题意

  给定一个元素个数为$N(1 \le N \le 10^5)$初始序列$a$和$b$,$a$序列的初始值全为$0$,$b$序列的初始值为$1$到$N$的一个排列。有$T(1 \le T \le 10^5)$次操作。操作有如下两种类型:

1、$add\ l\ r$:给序列$a$的区间$[l, r]$内所有元素加$1$;

2、$query\ l\ r$:查询$\sum\limits_{i=l}^{r}\lfloor{\frac{a_i}{b_i}}\rfloor$,并输出。

二、简要思路概括

  因为所有操作的总和$tot \le \sum\limits_{i=1}^{N}\frac{T}{i} = T * \sum\limits_{i=1}^{N}\frac{1}{i}$,而可证明调和级数$T * \sum\limits_{i=1}^{N}\frac{1}{i} \le TlogN$。

  所以,建立两棵线段树。一棵树$B$维护$b$序列的区间最小值,可支持区间修改。另一棵树$C$维护$\lfloor{\frac{a_i}{b_i}}\rfloor$的区间和。

  每次遇到$add$操作时,将$B$线段树的区间$[l, r]$减$1$,时间复杂度$O(logN)$。然后,再对线段树$B$做一遍dfs,如果有叶子节点的值为$0$,则把线段树$C$的对应位置加$1$,同时将该位置的值设置为初始的$b$值。时间复杂度$O(N)$。

  所以,单次$add$操作的时间复杂度是$O(N)$,总的时间复杂度是$O(N*T)$。但是,不要觉得会超时。实际的计算量并没有这么大。因为前面已证明,最多只会累加$T \times logN$次。所以,只要代码写得优雅,绝对不会超时。另外,还有一种写法,就是每次对线段树$B$做区间减$1$之后,循环查询$B$线段树的$[l, r]$区间内的最小值,如果最小值为$0$,则把线段树$C$的对应位置加$1$,同时修改该位置的值为初始的$b$值,直到该区间内的最小值不为$0$为止。这样,总的时间复杂度是$O(T*NlogN)$,也不会超时。

三、代码

#include<bits/stdc++.h>
using namespace std;
#define MAXN 100010
typedef long long ll;
struct Node1 {
    int mv, lazy;
};
struct Node2 {
    int v, sum;
};
Node1 b[MAXN * 4];
Node2 c[MAXN * 4];
int b0[MAXN], N, Q;

void push_down(int rt) {
    if(b[rt].lazy) {
        int lch = rt << 1, rch = rt << 1 | 1;
        b[lch].mv += b[rt].lazy, b[lch].lazy += b[rt].lazy;
        b[rch].mv += b[rt].lazy, b[rch].lazy += b[rt].lazy;
        b[rt].mv = min(b[rt << 1].mv, b[rt << 1 | 1].mv);
        b[rt].lazy = 0;
    }
}

void update1(int ul, int ur, int val, bool f, int rt = 1, int l = 1, int r = N) {
    if(l >= ul && r <= ur) {
        if(f)b[rt].mv = val;
        else b[rt].mv += val, b[rt].lazy += val;
        return;
    }
    push_down(rt);
    int mid = (l + r) >> 1;
    if(ul <= mid)update1(ul, ur, val, f, rt << 1, l, mid);
    if(ur > mid)update1(ul, ur, val, f, rt << 1 | 1, mid + 1, r);
    b[rt].mv = min(b[rt << 1].mv, b[rt << 1 | 1].mv);
}

void update2(int p, int rt = 1, int l = 1, int r = N) {
    if(l == r && l == p) {
        c[rt].v++, c[rt].sum++;
        return;
    }
    int mid = (l + r) >> 1;
    if(p <= mid)update2(p, rt << 1, l, mid);
    else update2(p, rt << 1 | 1, mid + 1, r);
    c[rt].sum = c[rt << 1].sum + c[rt << 1 | 1].sum;
}

int query2(int ql, int qr, int rt = 1, int l = 1, int r = N) {
    if(ql <= l && qr >= r)return c[rt].sum;
    int mid = (l + r) >> 1, res = 0;
    if(ql <= mid)res += query2(ql, qr, rt << 1, l, mid);
    if(qr > mid) res += query2(ql, qr, rt << 1 | 1, mid + 1, r);
    return res;
}

void dfs(int ul, int ur, int rt = 1, int l = 1, int r = N) {
    if(l == r) {
        b[rt].mv = b0[l], update2(l);
        return;
    }
    push_down(rt);
    int mid = (l + r) >> 1;
    if(ul <= mid && b[rt << 1].mv == 0)dfs(ul, ur, rt << 1, l, mid);
    if(ur > mid && b[rt << 1 | 1].mv == 0)dfs(ul, ur, rt << 1 | 1, mid + 1, r);
    b[rt].mv = min(b[rt << 1].mv, b[rt << 1 | 1].mv);
}

int main() {
//    freopen("input.txt", "r", stdin);
    char cmd[20];
    int l, r;
    while(~scanf("%d%d", &N, &Q)) {
        for(int i = 0, tt = N << 2; i < tt; ++i)b[i].mv = INT_MAX, b[i].lazy = 0;
        memset(c, 0, sizeof(c));
        for(int i = 1; i <= N; ++i)scanf("%d", b0 + i), update1(i, i, b0[i], 1);
        while(Q--) {
            scanf("%s%d%d", cmd, &l, &r);
            if(cmd[0] == 'a') {
                update1(l, r, -1, 0);
                dfs(l, r);
            }
            else cout << query2(l, r) << endl;
        }
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/565261641-fzh/p/9369264.html