HDU 5828 Rikka with Sequence 【线段树区间更新中单点更新】 好题!!!

传送门
题目大意:
有三种操作: 1. 区间开根 2.区间加 3.询问区间和

思路: 如果没有第二种操作, 就非常简单了, BZOJ上面有一道就是这种题, 因为开根的话每个数会下降的很快, 所以暴力的搞也不会搞太久, 但是有了区间加就不一样了.. 比如 3 4 3 4 3 4 3 4 …. 这段区间暴力搞的话, 会变成 1 2 1 2 1 2 1 2 …. 但是如果在区间加2, 有变成3 4 3 4 3 4 3 4 …. 了, 这样不断搞就会把你卡超时.. 所以我们需要转化. 当这个区间的max - min == 1时并且开完根后的max - min == 1的话, 那么我们就可以做区间减法, 而不是暴力的修改. 像刚才那个就是区间-2即可. 或者是区间max == min 在开根前后, 那么我们就可以是做区间覆盖…

这道题非常卡时. 所以还是加一个外挂吧.
AC Code

const int maxn = 1e5 + 5;
ll a[maxn], n, m;
inline void read(ll &x) {
    static char c;
    while(!isdigit(c = getchar()));
    x = c - '0';
    while( isdigit(c = getchar()))
        x = x * 10 + (c - '0');
}
struct Tree {
    int tl, tr; ll val, lazyadd, lazyset, mx, mi;
    void funadd(ll tmp) {
        lazyadd += tmp;
        val += (tr - tl + 1) * tmp;
        mx += tmp; mi += tmp;
    }
    void funset(ll tmp) {
        lazyset = tmp; lazyadd = 0;
        val = (tr - tl + 1) * tmp;
        mx = mi = tmp;
    }
}tre[maxn<<2];
void pushup(int id) {
    tre[id].val = tre[id<<1].val+tre[id<<1|1].val;
    tre[id].mx = max(tre[id<<1].mx, tre[id<<1|1].mx);
    tre[id].mi = min(tre[id<<1].mi, tre[id<<1|1].mi);
}
void pushdown(int id) {
    if(tre[id].lazyset != 0) {
        tre[id<<1].funset(tre[id].lazyset);
        tre[id<<1|1].funset(tre[id].lazyset);
        tre[id].lazyset = 0;
    }
    if(tre[id].lazyadd != 0) {
        tre[id<<1].funadd(tre[id].lazyadd);
        tre[id<<1|1].funadd(tre[id].lazyadd);
        tre[id].lazyadd = 0;
    }
}
void build(int id,int l,int r) {
    tre[id].tl = l; tre[id].tr = r;
    tre[id].lazyadd = tre[id].lazyset = 0;
    if(l == r) {
        read(a[l]);
        tre[id].val = tre[id].mx = tre[id].mi = a[l];
        return ;
    }
    int mid = (l+r) >> 1;
    build(id<<1, l, mid);
    build(id<<1|1, mid+1, r);
    pushup(id);
}
void update(int id, int ul, int ur, ll val) {
    int l = tre[id].tl, r = tre[id].tr;
    if(ul <= l && r <= ur) {
        tre[id].funadd(val);
        return ;
    }
    pushdown(id);
    int mid = (l+r) >> 1;
    if(ul <= mid) update(id<<1, ul, ur, val);
    if(ur > mid) update(id<<1|1, ul, ur, val);
    pushup(id);
}
bool check(int id) {
    if(tre[id].mx - tre[id].mi <= 1)
        return true;
    return false;
}
void ok(int id, int ul, int ur) {
    int l = tre[id].tl, r = tre[id].tr;
    if (tre[id].mx <= 1) return ;
    if(ul <= l && r <= ur && check(id)) {
        ll t1 = sqrt(tre[id].mx+0.1);
        ll t2 = sqrt(tre[id].mi+0.1);
        if (t1 == t2) tre[id].funset(t1);
        else tre[id].funadd(t1 - tre[id].mx);
        return ;
    }
    pushdown(id);
    int mid = (l+r) >> 1;
    if(ul <= mid) ok(id<<1, ul, ur);
    if(ur > mid) ok(id<<1|1, ul, ur);
    pushup(id);
}
ll query_sum(int id, int ql, int qr) {
    int l = tre[id].tl , r = tre[id].tr;
    if(ql <= l && r <= qr) {
        return tre[id].val;
    }
    pushdown(id);
    int mid = (l+r) >> 1 ;
    if(qr <= mid) return query_sum(id<<1, ql, qr);
    else if(ql > mid) return query_sum(id<<1|1, ql, qr);
    else return query_sum(id<<1, ql, mid) + query_sum(id<<1|1, mid+1, qr);
}

void out(ll a) {
    if(a > 9) out(a / 10);
    putchar(a % 10 + '0');
}
void solve() {
    read(n); read(m);
    build(1, 1, n);
    while(m--) {
        ll op, l, r;
        read(op); read(l); read(r);
        if (op == 1) {
            ll x; read(x);
            update(1, l, r, x);
        }
        else if (op == 2) ok(1, l, r);
        else {
            out(query_sum(1, l, r)); putchar('\n');
        }
    }
}

你可能感兴趣的:(线段树/RMQ/扫描线)