HDU 3954 level up 线段树

转载请注明出处,谢谢 http://blog.csdn.net/ACM_cxlove?viewmode=contents by---cxlove

HDU 3954 level up

http://acm.hdu.edu.cn/showproblem.php?pid=3954

HH的神题,想当初阿里巴巴这比赛是第一次参加的网上的比赛,还是组队的。结果被HH的一套神题完虐

一直都很崇拜HH大牛,

1、第一位开启“网吧通宵刷题模式”的队员(注:当时大一不允许带电脑,在一片充满游戏、电影以及美女图片的屏幕中能看到有人敲代码,你不觉得很酷吗?),此热情深深影响一届人;
2、第一位在第一学期就担任集训队副队长的队员;
3、第一位在大一就获得省赛金牌的队员(大学之前0基础);
4、第一位本科期间入围百度之星总决赛的队员(2010、2011年2次入围百度之星总决赛);
5、第一位大二就参加World Final的队员;
6、第一位2度参加World Final的队员(大二、大三参加World Final,无奈大四只能退役);
7、第一位带领队伍省赛捧杯的队员(2011年浙江省第8届省赛);
8、第一位大学4年3夺省赛金牌的队员;
……

不仅仅因为HH取得的成绩,他的精神感染了我,从零基础成为一名在ACM界赫赫有名的大牛。

言归正传,回到题目,区间更新,获得一定的经验可以升级,而每次获得的经验是和等级有关的,这是个难点。

区间lazy是必然的,否则必然TLE。而每次的lazy和以前不一样,由于之后的经验和等级有关,之前lazy的部分可能会导致升级,那之后的就不能按原来的lazy了。

只需要计算区间里的最大等级,经验,以及距离升级需要最小的经验基数(而不是经验总量,每次获得的经验是基数乘以等级的)。

/*
ID:cxlove
PROB:hdu 3954
DATA:2012.5.8
HINT:线段树
*/ 
#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
#include<algorithm>
#define LL long long
using namespace std;
int sum[15],n,k,q;
struct Line{
    int left,right,mid;
    int exp,level;   //区间最大的经验,等级 
    int min_dist,lazy;   //区间升级所需的最少经验基数,懒惰标记 
}L[50005];
//建树
void bulid(int step,int l,int r){
    L[step].left=l;
    L[step].right=r;
    L[step].mid=(l+r)/2;
    L[step].min_dist=sum[1];    //初始等级为1,所以经验基数便是sum[1]/1 
    L[step].exp=0;         //初始经验为0 
    L[step].level=1;         //初始等级为1 
    L[step].lazy=0;
    if(l==r)
        return;
    bulid(2*step,l,(l+r)/2);
    bulid(2*step+1,(l+r)/2+1,r);
}
//向下更新,lazy更新到子节点
void PushDown(int step){
    L[2*step].exp+=L[step].lazy*L[2*step].level;
    L[2*step].min_dist-=L[step].lazy;
    L[2*step].lazy+=L[step].lazy;

    L[2*step+1].exp+=L[step].lazy*L[2*step+1].level;
    L[2*step+1].min_dist-=L[step].lazy;
    L[2*step+1].lazy+=L[step].lazy;

    L[step].lazy=0;
}
//向上更新,更新了子节点后,更新父节点
void PushUp(int step){
    L[step].exp=max(L[2*step].exp,L[2*step+1].exp);
    L[step].level=max(L[2*step].level,L[2*step+1].level);
    L[step].min_dist=min(L[2*step].min_dist,L[2*step+1].min_dist);
}
//更新操作
void update(int step,int l,int r,int e){
    //叶子节点
    if(L[step].left==L[step].right){
        L[step].exp+=L[step].level*e;   
        while(L[step].exp>=sum[L[step].level])
            L[step].level++;
        //计算还需要多少经验基数升级
        L[step].min_dist=(sum[L[step].level]-L[step].exp)/L[step].level+((sum[L[step].level]-L[step].exp)%L[step].level!=0);
        return ;
    }
    if(L[step].left==l&&L[step].right==r){
        //区间内有英雄要升级了
        if(e>=L[step].min_dist){
            //lazy更新子节点 
            PushDown(step);
            //更新子区间 
            update(2*step,l,(l+r)/2,e);
            update(2*step+1,(l+r)/2+1,r,e);
            //更新父节点 
            PushUp(step);
        }
        //当前没有英雄升级 
        else{
            L[step].exp+=L[step].level*e;
            L[step].min_dist-=e;
            L[step].lazy+=e;
        }
        return ;
    }
    //向下更新 
    if(L[step].lazy)
        PushDown(step);
    if(r<=L[step].mid)
        update(2*step,l,r,e);
    else if(l>L[step].mid)
        update(2*step+1,l,r,e);
    else{
        update(2*step,l,L[step].mid,e);
        update(2*step+1,L[step].mid+1,r,e);
    }
    PushUp(step);
}
int query(int step,int l,int r){
    if(L[step].left==l&&L[step].right==r)
        return L[step].exp;
    if(L[step].lazy)
        PushDown(step);
    if(r<=L[step].mid)
        return query(2*step,l,r);
    else if(l>L[step].mid)
        return query(2*step+1,l,r);
    else
        return max(query(2*step,l,L[step].mid),query(2*step+1,L[step].mid+1,r));
}
int main(){
    int t,cas=0;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d%d",&n,&k,&q);
        for(int i=1;i<k;i++)
            scanf("%d",&sum[i]);
        sum[k]=1<<30;   //这里不可少 
        printf("Case %d:\n",++cas);
        bulid(1,1,n);
        while(q--){
            char str[10];
            int x,y,e;
            scanf("%s",str);
            if(str[0]=='W'){
                scanf("%d%d%d",&x,&y,&e);
                update(1,x,y,e);
            }
            else{
                scanf("%d%d",&x,&y);
                printf("%d\n",query(1,x,y));
            }
        }
        printf("\n");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值