HDU 4107 Gangster

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

题意:给定一个数组,初始时数组的值全为0 , 每次给数组中的一段区间增加一个值,若此区间中原先的值就已经超过了P,则该数增加2*val,否则增加val。求若干次增值之后数组最终的元素的值。

算法:线段树 + lazy优化


代码:

/*
HDU 4107 Gangster
Tips : segment Tree + lazy
runtime : 810ms     
*/
#include<stdio.h>
#include<string.h>
#define LL(x) (x<<1)
#define RR(x) ((x<<1)|1)
#define MID(a,b) ( (a+b)>>1 )
#define MAX 200001
#define MIN(a,b) (a>b?b:a)
struct Node{
    int L,R ;           //左右端点的界限
    int rank , exp ;    //叶子节点的属性 rank表示叶子节点的等级,exp表示叶子节点的当前伤害值
    int min_dis ;       //区间的属性,表示该区间内与p的值相差最小的值  
    int lazy ;          //区间属性,表示该区间内每个元素都增加的伤害值,也是lazy优化
}NN[4*MAX] ;
int n,m,p;
int ans[MAX] ;
//建树
void Build(int t,int l,int r){          
    int nl = LL(t) , nr = RR(t) ;
    NN[t].L = l ; NN[t].R = r ;
    NN[t].rank = 1 ; NN[t].exp = 0 ;
    NN[t].min_dis = p ;
    NN[t].lazy = 0 ;
    if(l == r) return ;
    int mid = MID(l,r);
    Build(nl,l,mid) ;
    Build(nr,mid+1,r) ;
}
//自下而上更新区间的dis_min的值
inline void search(int t){
    int nl = LL(t) , nr = RR(t) ;
    NN[t].min_dis = MIN(NN[nl].min_dis , NN[nr].min_dis) ;
}
//将该区间内的lazy值释放给它的字节点区间
inline void down(int t){
    int nl=LL(t) , nr = RR(t) ;
    if(NN[nl].L == NN[nl].R){
        NN[nl].exp += NN[nl].rank * NN[t].lazy ;
    }
    NN[nl].lazy += NN[t].lazy ;
    NN[nl].min_dis -= NN[t].lazy ;
    if(NN[nr].L == NN[nr].R){
        NN[nr].exp += NN[nr].rank * NN[t].lazy ;
    }
    NN[nr].lazy += NN[t].lazy ;
    NN[nr].min_dis -= NN[t].lazy ;

    NN[t].lazy = 0 ;
}
//线段树的更新,表示在[l,r]区间内增加一个val的值
inline void update(int t,int l,int r,int val){
    int mid = MID(NN[t].L,NN[t].R) ;
    int nl = LL(t) , nr = RR(t) ;
    if( NN[t].L == NN[t].R ){               //到达了叶子节点
        NN[t].exp += NN[t].rank * val ;         //计算叶子节点的exp值,并判断是否需要升级
        if(NN[t].exp >= p){         //叶子节点需要升级
            NN[t].rank = 2 ;
            NN[t].min_dis = (1<<30) ;
        }
        else{                       //不需要升级
            NN[t].min_dis = p-NN[t].exp ;
        }
        return ;
    }
    if(NN[t].L==l && NN[t].R==r){           //查询到了整个的区间
        if(NN[t].min_dis <= val){           //区间内又需要升级的叶子节点,此时不能直接lazy,需要释放lazy的值
            down(t);                //释放原先的lazy
            update(nl,NN[nl].L,NN[nl].R,val) ;
            update(nr,NN[nr].L,NN[nr].R,val) ;
            search(t);              //更新此区间的dis_min
        }
        else{                       //直接更新lazy即可
            NN[t].lazy += val ;
            NN[t].min_dis -= val ;
        }
        return ;
    }
    if(NN[t].lazy)              //没有找到完整的区间,需要继续递归求解,先释放lazy的值
        down(t) ;
    if(r<=mid)  update(nl,l,r,val);
    else if(l>=mid+1)   update(nr,l,r,val) ;
    else{
        update(nl,l,mid,val);
        update(nr,mid+1,r,val) ;
    }
    search(t) ;
    return ;
}
//将所有的值都询问出来
void query(int t,int l,int r){
    int nl = LL(t) , nr = RR(t) ;
    if(NN[t].L == NN[t].R) {
        ans[l] = NN[t].exp ;
        return ;
    }
    if(NN[t].lazy)  down(t) ;
    int mid = MID(NN[t].L,NN[t].R) ;
    query(nl,l,mid) ;
    query(nr,mid+1,r) ;
}
//输入外挂
inline void scan(int &n){
    char cc ;
    while(cc=getchar() , cc<'0'|| cc>'9') ;
    n = cc - '0' ;
    while(cc=getchar() , cc>='0' && cc<='9')
        n = n * 10 + cc - '0' ;
}
int main(){
    int L,R,val ;
    while(scanf("%d %d %d",&n,&m,&p)!=EOF){
        Build(1,1,n);
        for(int i=1;i<=m;i++){
            scan(L) ; scan(R); scan(val);
            update(1,L,R,val) ;
        }
        query(1,1,n) ;
        for(int i=1;i<=n;i++){
            if(i!=1)    printf(" ");
            printf("%d",ans[i]);
        }
        printf("\n");
    }
    return 0 ;
}



你可能感兴趣的:(HDU 4107 Gangster)