JZOJ4724. 斐波那契

题目大意

定义 Fi 表示斐波那契数列的第 i 项。
给定一个长度为n的序列 a ,和m个操作。操作有两种:

  • 1 L R 修改操作,表示给 ai,(LiR) 加上 FiL+1
  • 2 L R 询问操作,表示询问 Ri=Lai mod (1e9+9) 的值

Data Constraint
n,m105

题解

引入一种算法——定期重构


定期重构

每次的修改操作我们不直接修改而是先将操作存起来,当已经储存了 Size (一般取 Size=n )个操作时,再进行修改(重构)。每次查询,先查询已经重构的数组,再遍历已经存储的操作,每个操作单独计算贡献。
非常好证明,时间复杂度是 O(nn) ,运用定期重构可以很好地平衡修改与查询的时间复杂度。


注意到这题,我们的一个修改操作 [L,R] 可以做到 O(1) ,具体来说:
Deli 表示第 i 个元素修改后需要加的值,那么对[L,R]修改相当于:

  • DelL=DelL+1
  • DelR+1=DelR+1FRL+2
  • DelR+2=DelR+2FRL+1
  • i>1,Deli=Deli+Deli1+Deli2

这样一来修改就能做到 O(1) 了,但是询问还是 O(n) 的。所以我们可以用到上文提到的定期重构来平衡复杂度。问题完美解决。

时间复杂度: O(nn)

SRC

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std ;

#define N 100000 + 10
typedef long long ll ;
const int MO = 1e9 + 9 ;
struct Note {
    int l , r ;
    Note ( int L = 0 , int R = 0 ) { l = L , r = R ; }
} C[N] ;

int F[N] , S[N] ;
int a[N] , sum[N] , del[N] ;
int n , m , top , Size ;

void ReBuild() {
    a[1] = (a[1] + del[1]) % MO ;
    sum[1] = a[1] ;
    for (int i = 2 ; i <= n ; i ++ ) {
        del[i] = ((del[i] + del[i-1]) % MO + del[i-2]) % MO ;
        a[i] = (a[i] + del[i]) % MO ;
        sum[i] = (sum[i-1] + a[i]) % MO ;
    }
    memset( del , 0 , sizeof(del) ) ;
}

int main() {
    scanf( "%d%d" , &n , &m ) ;
    Size = sqrt(n) ;
    for (int i = 1 ; i <= n ; i ++ ) {
        scanf( "%d" , &a[i] ) ;
        sum[i] = (sum[i-1] + a[i]) % MO ;
    }
    F[1] = F[2] = 1 ;
    S[1] = 1 , S[2] = 2 ;
    for (int i = 3 ; i <= n ; i ++ ) {
        F[i] = (F[i-1] + F[i-2]) % MO ;
        S[i] = (S[i-1] + F[i]) % MO ;
    }
    for (int i = 1 ; i <= m ; i ++ ) {
        int op , L , R ;
        scanf( "%d%d%d" , &op , &L , &R ) ;
        if ( op == 1 ) {
            C[++top] = Note( L , R ) ;
            del[L] = (del[L] + 1) % MO ;
            del[R+1] = (del[R+1] - F[R-L+2] + MO) % MO ;
            del[R+2] = (del[R+2] - F[R-L+1] + MO) % MO ;
            if ( top == Size ) {
                ReBuild() ;
                top = 0 ;
            }
        } else {
            int ans = (sum[R] - sum[L-1] + MO) % MO ;
            for (int i = 1 ; i <= top ; i ++ ) {
                if ( C[i].r < L || C[i].l > R ) continue ;
                if ( L >= C[i].l && R <= C[i].r ) ans = (ans + (S[R-C[i].l+1] - S[L-C[i].l] + MO) % MO) % MO ;
                else if ( C[i].l >= L && C[i].r <= R ) ans = (ans + S[C[i].r-C[i].l+1]) % MO ;
                else if ( C[i].l >= L ) ans = (ans + S[R-C[i].l+1]) % MO ;
                else ans = (ans + (S[C[i].r-C[i].l+1] - S[L-C[i].l] + MO) % MO) % MO ;
            }
            printf( "%d\n" , ans ) ;
        }
    }
    return 0 ;
}

以上.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值