HDU 3954 level up (线段树)

题目链接
题目大意: 有n个角色,k个等级,q个询问。一开始所有角色经验为0,等级为1.可以执行两种操作。输入W l r e吧[l,r]区间内的角色经验都加上 等级*e,输入Q l r查询[l,r]区间内最大的经验值。
思路:看到题第一个想法就是维护区间内最大的等级和经验值,然后但会遇到这么一个问题,区间修改的时候肯定要打懒标记,但是如果在遍历线段树时要在一个已经打过标记的结点上再打标记,那么这时维护的信息是不正确的。因为你可能上次标记的时候这个区间的值已经变换,所获得的经验也会有变化,所以懒标记不可以直接累加。
既然不可以累加那么该怎么办,考虑到如果当前累加上标记之后如果等级没有提升那么下次还是可以直接累加标记的。但如果等级提升了,就要下传一次标记在叶子节点处进行修改。所以我们要维护的信息就多了一个,即当前区间里距离升级所需要的最少经验指数(这里是指的不是所需经验值,而是和传入的e属于同一类型的数据)。
理解了这一个最关键的点就可以写代码了。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int N=1e4+10;
struct node
{
    int l,r;
    int mx,le;//最大经验以及它的等级
    int lazy,dis;//懒标记,距离升级的经验指数
}t[50005];
int sum[15],n,m,q;
inline void pushup(int k)
{
    t[k].dis=min(t[k<<1].dis,t[k<<1|1].dis);
    t[k].mx=max(t[k<<1].mx,t[k<<1|1].mx);
    t[k].le=max(t[k<<1].le,t[k<<1|1].le);
}
inline void pushdown(int k)
{
    if(t[k].lazy)
    {
        t[k<<1].mx+=t[k<<1].le*t[k].lazy;
        t[k<<1].lazy+=t[k].lazy;
        t[k<<1].dis-=t[k].lazy;

        t[k<<1|1].mx+=t[k<<1|1].le*t[k].lazy;
        t[k<<1|1].lazy+=t[k].lazy;
        t[k<<1|1].dis-=t[k].lazy;

        t[k].lazy=0;
    }
}
void build(int k,int l,int r)
{
    t[k].l=l;
    t[k].r=r;
    t[k].lazy=0;
    t[k].dis=sum[1];
    t[k].mx=0;
    t[k].le=1;
    if(l==r) return;
    else
    {
        int mid=(l+r)>>1;
        build(k<<1,l,mid);
        build(k<<1|1,mid+1,r);
    }
}
void update(int k,int l,int r,int e)
{
    if(t[k].l==t[k].r)//叶子结点
    {
        t[k].mx+=t[k].le*e;
        while(sum[t[k].le]<=t[k].mx) ++t[k].le;
        t[k].dis=(sum[t[k].le]-t[k].mx)/t[k].le+((sum[t[k].le]-t[k].mx)%t[k].le!=0);
        return;
    }
    else if(t[k].l==l&&t[k].r==r)
    {
        if(e>=t[k].dis)
        {
            pushdown(k);
            int mid=(l+r)>>1;
            update(k<<1,l,mid,e);
            update(k<<1|1,mid+1,r,e);
            pushup(k);
        }
        else
        {
            t[k].mx+=t[k].le*e;
            t[k].lazy+=e;
            t[k].dis-=e;
        }
        return;
    }
    else
    {
        pushdown(k);
        int mid=(t[k].l+t[k].r)>>1;
        if(r<=mid) update(k<<1,l,r,e);
        else if(l>mid) update(k<<1|1,l,r,e);
        else
        {
            update(k<<1,l,mid,e);
            update(k<<1|1,mid+1,r,e);
        }
        pushup(k);
    }
}
int query(int k,int l,int r)
{
    if(t[k].l==l&&t[k].r==r) return t[k].mx;
    else
    {
        pushdown(k);
        int mid=(t[k].l+t[k].r)>>1;
        if(r<=mid) return query(k<<1,l,r);
        else if(l>mid) return query(k<<1|1,l,r);
        else return max(query(k<<1,l,mid),query(k<<1|1,mid+1,r));
    }
}
int main()
{
    int T,cas=0;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d",&n,&m,&q);
        for(int i=1;i<m;++i) scanf("%d",&sum[i]);
        sum[m]=(1<<30);
        int l,r,e;
        printf("Case %d:\n",++cas);
        build(1,1,n);
        while(q--)
        {
            char s[5];
            scanf("%s",s);
            if(s[0]=='W')
            {
                scanf("%d%d%d",&l,&r,&e);
                update(1,l,r,e);
            }
            else
            {
                scanf("%d%d",&l,&r);
                printf("%d\n",query(1,l,r));
            }
        }
        printf("\n");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值