[hdu4893] Wow! Such Sequence!(线段树)

题目大意

描述

维护一个n个数字的数列,最初所有的数字都是零,有三种操作:
1.将数列的第k个数字加d。
2.查询 ai a i 的和,其中 lir l ≤ i ≤ r
3.将 ai a i 更改为最近的Fibonacci数,其中 lir l ≤ i ≤ r

F0=1F1=1 F 0 = 1 , F 1 = 1 ,对于 n2 n ≥ 2 ,斐波纳契数 Fn F n 定义为 Fn=Fn1+Fn2 F n = F n − 1 + F n − 2
x x 的最近斐波那契数表示最小的Fn,其中 |Fnx| | F n − x | 也是最小的。

样例输入
1 1
2 1 1
5 4
1 1 7
1 3 17
3 2 4
2 1 5
样例输出
0
22
数据范围

1n100000,1m100000|d|<231 1 ≤ n ≤ 100000 , 1 ≤ m ≤ 100000 , | d | < 2 31

解题思路

用线段树来维护这个序列。
我们发现,如果一个数已经被修改成了一个斐波那契数,再对其进行修改并不会影响它的值,所以我们可以对线段树每个节点打一个标记表示这个节点表示的区间是否都是斐波那契数,这样,每次区间修改时,如果标记为“是”就不用修改,只需对标记为“否”的节点进行修改。

Code

#include<cstdio>
#include<cstring>
#include<algorithm>

#define lid id<<1
#define rid id<<1|1
#define mid ((tr[id].l+tr[id].r)>>1)

using namespace std;

typedef long long LL;

const int N = 100005;
int n, m, opt, ql, qr, dex;
LL f[N];

struct seg_tree{
    int l, r;
    LL sum;
    bool fib;
    void init(){
        l = r = 0;
        sum = 0;
        fib = false;
    }
}tr[N<<2];

inline LL getFib(LL x){
    if(x == 0)  return 1;
    int t = lower_bound(f, f+101, x) - f;
    if(x - f[t-1] > f[t] - x)   return f[t];
    return f[t-1];
}

inline void pushup(int id){
    tr[id].sum = tr[lid].sum + tr[rid].sum;
    tr[id].fib = tr[lid].fib & tr[rid].fib;
}

void build(int id, int l, int r){
    tr[id].init();
    tr[id].l = l, tr[id].r = r;
    if(tr[id].l == tr[id].r)    return;
    build(lid, l, mid);
    build(rid, mid+1, r);
    pushup(id);
}

void add(int id, int pos, LL val){
    if(tr[id].l == tr[id].r){
        tr[id].sum += val;
        tr[id].fib = false;
        return;
    }
    if(pos <= mid)  add(lid, pos, val);
    else    add(rid, pos, val);
    pushup(id);
}

LL query(int id, int l, int r){
    if(tr[id].l == l && tr[id].r == r)  return tr[id].sum;
    if(r <= mid)    return query(lid, l, r);
    else if(l > mid)    return query(rid, l, r);
    else    return query(lid, l, mid) + query(rid, mid+1, r);
}

void change(int id, int l, int r){
    if(tr[id].l == tr[id].r){
        tr[id].sum = getFib(tr[id].sum);
        tr[id].fib = true;
        return;
    }
    if(r <= mid)    change(lid, l, r);
    else if(l > mid)    change(rid, l, r);
    else{
        if(!tr[lid].fib)    change(lid, l, mid);
        if(!tr[rid].fib)    change(rid, mid+1, r);
    }
    pushup(id);
}

int main(){
    f[0] = f[1] = 1;
    for(int i = 2; i <= 100; i++)   f[i] = f[i-1] + f[i-2];
    while(scanf("%d%d", &n, &m)!=EOF){
        build(1, 1, n);
        while(m--){
            scanf("%d%d%d", &opt, &ql, &qr);
            if(opt == 1)    add(1, ql, qr);
            else if(opt == 2)   printf("%lld\n", query(1, ql, qr));
            else if(opt == 3)   change(1, ql, qr);
        }
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值