codeforces 446C C. DZY Loves Fibonacci Numbers(线段树+菲波那契数的性质)

题目链接:

codeforces 446C


题目大意:

给出一个数列,每次可以选取一个区间,按顺序加上第i个菲波那契数进行更新,也可以查询某一个区间的总和。


题目分析:

  • 首先要做这个题必须了解菲波那契数的一些基本的性质
    • 首先我们是可以通过每个菲波那契数列的前两项 O(1) 的获得任意一项的数值和任意i项的前缀和。
    • 然后就是两个菲波那契数列相加之后依旧是一个菲波那契数列,只是前两项的值变化,分别变为了两个菲波那契数列前两项的和。
    • 利用这两个性质之后就是线段树很基本的操作了,用f1,f2懒操作当前要加的数列的前两项的值,sum记录当前区间的总和。
  • 下面简单证明前面提到的两个性质:
    • 第一性质证明如下:
      首先我们可以预处理除前两项都为1的菲波那契数列的各项的值。然后我们可以通过 afib[n1]+bfib[n2] 获得以a,b为前两项的第n项的值。因为我们可以先看a作为第一项,在要求的第n个数中出现了多少次?我们可以将第一项标记为1,第二项作为0,也就是a出现的次数,那么对于第i个数中存在a的个数,就等于fib[n-1],因为在做递推的过程中a的数目比fib的数值相当于慢了一步,关于b的个数同理可证,那么公式成立。
      然后我们对于菲波那契数中某一项:
      {fib[n]=fib[n1]+fib[n2]fib[n1]=fib[n2]+fib[n3]fib[n]=fib[n2]++fib[3]+2fib[2]+fib[1]i=1nfib[i]=fib[n+2]fib[2]

      第二个性质其实很容易证明,两个菲波那契数列相加只不过是修改了前两项的值,故不再赘述,有了这两个性质,就很方便来维护菲波那契数列的一些的操作了。

AC代码:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#define MAX 300007

using namespace std;

typedef long long LL;

int n,m,a[MAX];
const LL mod = 1e9+9;
LL fib[MAX];

struct Tree
{
    int l,r;
    LL sum,f1,f2;
}tree[MAX<<2];

void push_up ( int u )
{
    tree[u].sum = tree[u<<1].sum + tree[u<<1|1].sum;
    tree[u].sum %= mod;
}

void build ( int u , int l , int r )
{
    tree[u].l = l;
    tree[u].r = r;
    tree[u].f1 = tree[u].f2 = 0;
    if ( l == r )
    {
        tree[u].sum = a[l];
        return;
    }
    int mid = l+r>>1;
    build ( u<<1 , l , mid );
    build ( u<<1|1 , mid+1 , r );
    push_up ( u );
}

void init ( )
{
    fib[1] = fib[2] = 1;
    for ( int i = 3 ; i < MAX ; i++ )
    {
        fib[i] = fib[i-1] + fib[i-2];
        fib[i] %= mod;
    }
}

LL get ( LL a , LL b , int n )
{
    if ( n == 1 ) return a%mod;
    if ( n == 2 ) return b%mod;
    return (a*fib[n-2]%mod+b*fib[n-1]%mod)%mod;
}

LL sum ( LL a , LL b , int n )
{
    if ( n == 1 ) return a;
    if ( n == 2 ) return (a+b)%mod;
    return ((get ( a , b , n+2 )-b)%mod+mod)%mod;
}

void push_down ( int u )
{
    int f1 = tree[u].f1;
    int f2 = tree[u].f2;
    int l = tree[u].l;
    int r = tree[u].r;
    int ll = (l+r)/2-l+1;
    int rr = r-(l+r)/2;
    if ( f1 )
    {
        if ( l != r )
        {
            tree[u<<1].f1 += f1;
            tree[u<<1].f1 %= mod;
            tree[u<<1].f2 += f2;
            tree[u<<1].f2 %= mod;
            tree[u<<1].sum += sum ( f1 , f2 , ll );
            tree[u<<1].sum %= mod;
            int x = f1 , y = f2;
            f2 = get ( x , y , ll+2 );
            f1 = get ( x , y , ll+1 );
            tree[u<<1|1].f2 += f2;
            tree[u<<1|1].f2 %= mod;
            tree[u<<1|1].f1 += f1;
            tree[u<<1|1].f1 %= mod;
            tree[u<<1|1].sum += sum ( f1 , f2 , rr );
            tree[u<<1|1].sum %= mod;
        }
        tree[u].f1 = tree[u].f2 = 0;
    }
}

void update ( int u , int left , int right )
{
    int l = tree[u].l;
    int r = tree[u].r;
    int mid = l+r>>1;
    if ( left <= l && r <= right )
    {
        tree[u].f1 += fib[l-left+1];
        tree[u].f1 %= mod;
        tree[u].f2 += fib[l-left+2];
        tree[u].f2 %= mod;
        int f1 = fib[l-left+1], f2 = fib[l-left+2];
        tree[u].sum += sum ( f1 , f2 , r-l+1 );
        tree[u].sum %= mod;
        return;
    }
    push_down ( u);
    if ( left <= mid && right >= l )
        update ( u<<1 , left , right );
    if ( left <= r && right > mid )
        update ( u<<1|1 , left , right );
    push_up ( u );
}

LL query ( int u , int left , int right )
{
    int l = tree[u].l;
    int r = tree[u].r;
    int mid = l+r>>1;
    if ( left <= l && r <= right )
        return tree[u].sum;
    push_down ( u );
    LL ret = 0;
    if ( left <= mid && right >= l ) 
    {
        ret += query ( u<<1 , left , right );
        ret %= mod;
    }
    if ( left <= r && right > mid )
    {
        ret += query ( u<<1|1 , left , right );
        ret %= mod;
    }
    return ret;
}

int main ( )
{
    init ( );
    while ( ~scanf ( "%d%d" , &n , &m ) )
    {
        for ( int i = 1; i <= n ; i++ )
            scanf ( "%d" , &a[i] );
        build ( 1 , 1 , n );
        while ( m-- )
        {
            int x,l,r;
            scanf ( "%d%d%d" , &x , &l , &r );
            if ( x == 1 )
                update ( 1 , l , r );
            else
                printf ( "%lld\n" , query ( 1 , l , r ) );
        }
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值