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

来源:互联网 发布:手机版进销存软件排行 编辑:程序博客网 时间:2024/06/07 01:49

题目链接:

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 300007using 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 ) );        }    }}
0 0
原创粉丝点击