hdu 4893(线段树 成段更新+区间合并)

题意:

给你大小为n的序列,该序列的初始值为0,有3种操作

1.序列的第K个位置增加d

2.询问一段区间的和

3.将一段区间中得每一个数值都改为一个最小的fibonacci数,

且该fibonacci数与区间内对应的值距离最近。


解题思路:

可以用线段树来解决该问题,且线段树中得域:

lazy标记:表示当前某区间更新的标记

sum:用于求某段区间的和

lin;表示某段区间中的每一个值距离其最近的fibo的差值

所以当我们更新到某段区间时,我们要将该段的lazy  = 1;

并要将sum+=lin,lin = 0;

注意:

要用__int64

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#define MAXN0 1<<64
#define l1(x) (x)<<1
#define l11(x) l1(x)|1
#define r1(x)  (x)>>1
#define MAXN1 100001
typedef __int64 LL;
LL F[MAXN1];
int n,m;
struct TRnode{
    int L,R;
    int lazy;
    LL sum;
    LL lin;
};
LL min(LL aa,LL bb){
    return aa<bb?aa:bb;
}
LL getbt(LL gx){
    int l,r,mid;
    l = 0;
    r = 91;
    while(l<r){
        mid = r1(l+r);
        if(F[mid]>=gx){
            r = mid;
        }
        else{
            l = mid+1;
        }
    }
    LL tmp1,tmp2,tmp0;
    if(l==0){
        tmp0 = F[l] - gx;
        if(tmp0<0)tmp0 = -tmp0;
        tmp1 = F[l+1] - gx;
        if(tmp1<0)tmp1 = -tmp1;
        return tmp0<=tmp1?F[l]:F[l+1];
    }
    else if(l==91){
        tmp0 = F[l] - gx;
        if(tmp0<0)tmp0 = -tmp0;
        tmp1 = F[l-1] - gx;
        if(tmp1<0)tmp1 = -tmp1;
        return tmp0<tmp1?F[l]:F[l-1];
    }
    else {
        tmp0 = F[l-1] - gx;
        if(tmp0<0)tmp0= -tmp0;
        tmp1 = l-1;
        for(int i=l;i<=l+1;++i){
            tmp2 = F[i] - gx;
            if(tmp2<0){
                tmp2 = -tmp2;
            }
            if(tmp2<tmp0){
                tmp1 = i;
                tmp0 = tmp2;
            }
        }
        return F[tmp1];
    }
}
TRnode TR[MAXN1<<2];
void buildTR(int L,int R,int k){
    TR[k].L = L;
    TR[k].R = R;
    TR[k].lazy = 0;
    if(L==R){
        TR[k].sum = 0;
        TR[k].lin = 1;
        return;
    }
    TR[k].sum = 0;
    int mid = r1(L+R);
    //TR[k].lin =
    buildTR(L,mid,l1(k));
    buildTR(mid+1,R,l11(k));
    TR[k].lin = TR[l1(k)].lin + TR[l11(k)].lin;
}
void pd(int k,int k1,int k11){
    TR[k1].lazy = TR[k11].lazy = TR[k].lazy;
    TR[k1].sum = TR[k1].lin + TR[k1].sum;
    TR[k11].sum = TR[k11].lin + TR[k11].sum;
    TR[k1].lin = TR[k11].lin = 0;
    TR[k].lazy = 0;
}
void pu(int k,int k1,int k11){
    TR[k].lin = TR[k1].lin + TR[k11].lin;
    TR[k].sum = TR[k1].sum + TR[k11].sum;
}
void update(int L,int R,int k,LL val,bool flag){
    if(TR[k].L==L&&TR[k].R==R){
        if(flag){
            TR[k].lazy = 1;
            TR[k].sum = TR[k].lin + TR[k].sum;
            TR[k].lin = 0;
        }
        else {
            if(TR[k].lazy){
                TR[k].sum=TR[k].sum+TR[k].lin;
            }
            TR[k].sum+=val;
            TR[k].lin = getbt(TR[k].sum) - TR[k].sum;
            TR[k].lazy = 0;
            //TR[k].sum +=val;
        }
        return;
    }
    if(TR[k].lazy){
        pd(k,l1(k),l11(k));
    }
    int mid = r1(TR[k].L+TR[k].R);
    if(mid>=R){
        update(L,R,l1(k),val,flag);
    }
    else if(mid<L){
        update(L,R,l11(k),val,flag);
    }
    else {
        update(L,mid,l1(k),val,flag);
        update(mid+1,R,l11(k),val,flag);
    }
    pu(k,l1(k),l11(k));
}
LL Query(int L,int R,int k){
    if(TR[k].L==L&&TR[k].R==R){
        return TR[k].sum;
    }
    if(TR[k].lazy){
        pd(k,l1(k),l11(k));
    }
    int mid = r1(TR[k].L+TR[k].R);
    if(mid>=R){
        return Query(L, R, l1(k));
    }
    else if(mid<L){
        return Query(L,R,l11(k));
    }
    else {
        return Query(L,mid,l1(k))+Query(mid+1, R, l11(k));
    }
}
int main(){
    F[0] = 1;
    F[1] = 1;
    //freopen("/Users/gatsby/desktop/oo.txt", "w", stdout);
    for(int i =2;i<=91;++i){
        F[i] = F[i-1] + F[i-2];
        //printf("%llu\n",F[i]);
    }
    int w;
    int k,L,R;
    LL d;
    while(scanf("%d%d",&n,&m)!=EOF){
        buildTR(1,n,1);
        while(m--){
            scanf("%d",&w);
            if(w==1){
                scanf("%d%I64d",&k,&d);
                update(k,k,1,d,false);
            }
            else if(w==2){
                scanf("%d%d",&L,&R);
                LL ans = Query(L, R, 1);
                printf("%I64d\n",ans);
            }
            else {
                scanf("%d%d",&L,&R);
                update(L,R,1,0,true);
            }
        }
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值