[线段树]HDU4893 Wow! Such Sequence!

题意:一个序列n个数初始为0,m次操作。操作1,把第k个加上d;操作2,对l到r的结点求和;操作3,把l到r的结点变成最近的斐波那契数,如果到左右的斐波数相同就选小的那个。

这题真是坑了好久了啊。

第一次用的树状数组做,因为是单点增减区间求和,而变成斐波那契数的操作显然必须对叶子结点操作,然后更新上去,超时果断的。

其实如果一段区间之前做了3操作,而之后这个区间没有做过1操作,再做3操作是可以直接忽略的,用线段树在这里打lazy标记,每次3操作就打上lazy,1操作就抹去lazy。

这样一想其实是个模版题。

具体看代码。

//请用G++ 
#include<cstdio>
#include<cmath> 
#include<algorithm>
#define LL __int64
#define lld I64d
using namespace std;
const int MN=100005;
LL fib[100]={1,1,2};
struct node{
    int l,r,m;
    bool lazy; //lazy表示当前结点的区间是否全部在进行过3操作之后没有1操作 
    LL val;
}tree[MN<<2];
int n,m;
void cfib(LL &val){ //二分 
    int p=lower_bound(fib,fib+100,val) -fib;
    if(val-fib[p-1] > fib[p]-val) val=fib[p];
    else val=fib[p-1]; 
}
void build(int c,int l,int r){
    tree[c].l=l,tree[c].r=r,tree[c].lazy=0,tree[c].val=0;
    if(l==r) return;
    tree[c].m=(l+r)>>1;
    build(c<<1,l,tree[c].m);
    build(c<<1|1,tree[c].m+1,r);
}
void update(int c,int pos,int val){
    tree[c].lazy=0; //抹去更新路径的lazy 
    if(tree[c].l==tree[c].r){
        tree[c].val+=val;
        return;
    }
    if(pos<=tree[c].m) update(c<<1,pos,val);
    else update(c<<1|1,pos,val);
    tree[c].val=tree[c<<1].val+tree[c<<1|1].val;
    tree[c].lazy=tree[c<<1].lazy&tree[c<<1|1].lazy;
}
void fibdown(int c){
    if(tree[c].lazy) return; //lazy标记过,当前结点的数已经是斐波数了 
    tree[c].lazy=1; // 打上lazy 
    if(tree[c].l==tree[c].r){
        if(!tree[c].val) tree[c].val=1;
        else cfib(tree[c].val);
        return;
    }
    fibdown(c<<1);
    fibdown(c<<1|1);
    tree[c].val=tree[c<<1].val+tree[c<<1|1].val;
}
void FIB(int c,int ql,int qr){
    if(tree[c].lazy) return;
    if(tree[c].l>=ql&&tree[c].r<=qr){
        fibdown(c); //这棵子树被完全覆盖,用fibdown函数更新这棵子树的叶子节点 
        return;
    }
    if(ql<=tree[c].m) FIB(c<<1,ql,qr);
    if(qr>tree[c].m) FIB(c<<1|1,ql,qr);
    tree[c].val=tree[c<<1].val+tree[c<<1|1].val;
    tree[c].lazy=tree[c<<1].lazy&tree[c<<1|1].lazy; //当前结点的lazy右左右儿子的lazy决定 
}
LL query(int c,int ql,int qr){
    if(tree[c].l>=ql&&tree[c].r<=qr){
        return tree[c].val;
    }
    LL res=0;
    if(ql<=tree[c].m) res+=query(c<<1,ql,qr);
    if(qr>tree[c].m) res+=query(c<<1|1,ql,qr);
    return res;
}
int main(){
    int a,b,c;
    for(int i=3;i<100;++i) fib[i]=fib[i-1]+fib[i-2];
    while(scanf("%d %d",&n,&m)!=EOF){
        build(1,1,n);
        while(m--){
            scanf("%d %d %d",&a,&b,&c);
            if(a==1) update(1,b,c);
            else if(a==2) printf("%lld\n",query(1,b,c));
            else FIB(1,b,c); 
        }
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值