hdu 3954 Level up 线段树 升级版Lazy tag 区间整体的性质

题目链接


题意:

给定一段区间 [1, n],每个点都有自身的 Level 值和 Exp 值;

进行若干次操作 W 和 Q,

W L R e:给区间 [L, R] 的每个点增加一定的经验 exp = Level * e, 注意到每个点增加的经验值是不同的,与各自的等级成正比

Q L R:询问区间 [L, R] 内的最大经验值 maxExp


思考过程:

因为每个点增加的经验值是不同的,所以很难直接对区间整体进行操作,所以像普通的 lazy tag 一样给区间整体的 sum 加上 val * len 的值,再给区间整体的 tag 加上 val 值是不可行的。而且 tag 还不能简单地叠加,因为也许这次操作完有一个点就升级了呢?而且向上 push_up 的话每一段区间的 Level 值 与 Exp 值也意义不明,毕竟每个点都不一样啊..........

于是一开始就傻乎乎地写成了单点修改,然后TLE不解释............


看了 http://blog.youkuaiyun.com/wsniyufang/article/details/6702560 后,豁然开朗


我们思考下放 tag 的条件:一旦该区间内有一个点升级,就下放 tag

再考虑 tag 的意义,tag 代表了一个区间整体的性质,这才使得整体的操作成为可能。

那么在这道题里,区间整体有什么性质呢?事实上,我们可以用 rem(ain) 记录每个点还差多少经验 e 即可升级,整个区间的 rem 取所有点的 rem 的最小值,一旦给这段区间加上的 exp 超过 rem 值,即下放 tag

那么如果这次操作不能使得这段区间的任何一个点升级呢?显然,我们不需要下放 tag,而是需要在现有的 tag 值上加上这次的 exp。再仔细想想,如果我下次恰好询问的就是该区间内的 maxExp 呢?那么按照我们写 query 的格式,会直接返回这一段的经验最大值。所以,这经验最大值不应该简简单单地只是每一次从下面 push 上来的整个区间内的 maxExp 这个数值(因为如果更新 exp 的途径只是从下面 push 上来,在这种情况下,返回的值就是没有更新过的旧值),而还应该具有其自身的含义——那就是这个区间内经验最大的那个点的经验值,即它要代表这个区间内经验最大的那个点。

好像说的是同一码事,事实上还是有差别的,如果其意义为 这个区间内经验最大的那个点的经验值,即它代表了这个区间内经验最大的那个点(同时也必然是等级最高的那个点),那么 对这一段整体经验值的增加 导致的 该段经验值最大的点的经验的增加 就直接可以通过 对该区间记录的 exp 值的修改反应出来。

总结一下我们上面说的,每个区间的 exp 与 lv 值就应该是该区间内 exp 值 最大的点的 exp 值 与 lv 值,它就完完全全地反应了作为了那个点的代言人存在。


这样一来,对于每种情况该如何处理也差不多清楚了,就可以写代码了............


AC代码如下:

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define maxn 10010
#define lson (rt << 1)
#define rson (rt << 1 | 1)
typedef long long LL;
struct node {
    int l, r, lv, rem;
    LL exp, tag;
}tr[maxn * 4];
int ned[20], n, k, q, kas;
inline LL max(LL a, LL b) { return a > b ? a : b; }
inline int max(int a, int b) { return a > b ? a : b; }
inline int midi(int a, int b) { return (a + b) >> 1; }
inline int min(int a, int b) { return a < b ? a : b; }
void push_up(int rt) {
    tr[rt].exp = max(tr[lson].exp, tr[rson].exp);
    tr[rt].rem = min(tr[lson].rem, tr[rson].rem);
    tr[rt].lv = max(tr[lson].lv, tr[rson].lv);
}
void push_down(int rt) {
    if (tr[rt].tag) {
        tr[lson].exp += (LL)tr[lson].lv * tr[rt].tag;
        tr[rson].exp += (LL)tr[rson].lv * tr[rt].tag;
        tr[lson].rem -= tr[rt].tag; tr[rson].rem -= tr[rt].tag;
        tr[lson].tag += tr[rt].tag; tr[rson].tag += tr[rt].tag;
        tr[rt].tag = 0;
    }
}
void build(int rt, int l, int r) {
    tr[rt].l = l; tr[rt].r = r; tr[rt].exp = 0; tr[rt].tag = 0; tr[rt].lv = 0;
    if (l == r) {
        tr[rt].lv = 1;
        tr[rt].rem = ned[1] / 1;
        return;
    }
    int mid = midi(l, r);
    build(lson, l, mid);
    build(rson, mid + 1, r);
    push_up(rt);
}
void modify(int rt, int l, int r, int exp) {
    if (tr[rt].l == tr[rt].r) {
        tr[rt].exp += (LL)exp * tr[rt].lv;
        while (tr[rt].lv < k && tr[rt].exp >= ned[tr[rt].lv]) ++tr[rt].lv;
        if (tr[rt].lv == k) tr[rt].rem = inf;
        else tr[rt].rem = ceil((double)(ned[tr[rt].lv] - tr[rt].exp) / tr[rt].lv);
        return;
    }
    if (tr[rt].l == l && tr[rt].r == r) {
        if (tr[rt].rem > exp) {
            tr[rt].exp += tr[rt].lv * exp;
            tr[rt].tag += exp;
            tr[rt].rem -= exp;
            return;
        }
        push_down(rt);
        int mid = midi(l, r);
        modify(lson, l, mid, exp);
        modify(rson, mid + 1, r, exp);
        push_up(rt);
        return;
    }
    push_down(rt);
    int mid = midi(tr[rt].l, tr[rt].r);
    if (r <= mid) modify(lson, l, r, exp);
    else if (l > mid) modify(rson, l, r, exp);
    else { modify(lson, l, mid, exp); modify(rson, mid + 1, r, exp); }
    push_up(rt);
    return;
}
LL query(int rt, int l, int r) {
    if (tr[rt].l == l && tr[rt].r == r) return tr[rt].exp;
    push_down(rt);
    int mid = midi(tr[rt].l, tr[rt].r);
    if (r <= mid) return query(lson, l, r);
    else if (l > mid) return query(rson, l, r);
    else return max(query(lson, l, mid), query(rson, mid + 1, r));
}
void work() {
    printf("Case %d:\n", ++kas);
    scanf("%d%d%d", &n, &k, &q);
    for (int i = 1; i < k; ++i) scanf("%d", &ned[i]);
    build(1, 1, n);
    while (q--) {
        char ch;
        scanf("\n%c", &ch);
        int l, r, exp;
        if (ch == 'Q') {
            scanf("%d%d", &l, &r);
            printf("%lld\n", query(1, l, r));
        }
        else {
            scanf("%d%d%d", &l, &r, &exp);
            modify(1, l, r, exp);
        }
    }
    printf("\n");
}
int main() {
//    freopen("3954.in", "r", stdin);
    int T;
    scanf("%d", &T);
    while (T--) work();
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值