af[n-1] + b[n-2] (a >= b 且a, b都是斐波那契数) 那么这个式子就表示 以a b为前两项的斐波那契数的第n项斐波那契数
http://blog.csdn.net/qq_24451605/article/details/48105763
题目链接:
题目大意:
给出一个数列,每次可以选取一个区间,按顺序加上第i个菲波那契数进行更新,也可以查询某一个区间的总和。
题目分析:
- 首先要做这个题必须了解菲波那契数的一些基本的性质
- 首先我们是可以通过每个菲波那契数列的前两项O(1)的获得任意一项的数值和任意i项的前缀和。
- 然后就是两个菲波那契数列相加之后依旧是一个菲波那契数列,只是前两项的值变化,分别变为了两个菲波那契数列前两项的和。
- 利用这两个性质之后就是线段树很基本的操作了,用f1,f2懒操作当前要加的数列的前两项的值,sum记录当前区间的总和。
- 下面简单证明前面提到的两个性质:
- 第一性质证明如下:
首先我们可以预处理除前两项都为1的菲波那契数列的各项的值。然后我们可以通过a⋅fib[n−1]+b⋅fib[n−2]获得以a,b为前两项的第n项的值。因为我们可以先看a作为第一项,在要求的第n个数中出现了多少次?我们可以将第一项标记为1,第二项作为0,也就是a出现的次数,那么对于第i个数中存在a的个数,就等于fib[n-1],因为在做递推的过程中a的数目比fib的数值相当于慢了一步,关于b的个数同理可证,那么公式成立。
然后我们对于菲波那契数中某一项:
{fib[n]=fib[n−1]+fib[n−2]fib[n−1]=fib[n−2]+fib[n−3]⇒fib[n]=fib[n−2]+⋯+fib[3]+2⋅fib[2]+fib[1]⇒∑i=1nfib[i]=fib[n+2]−fib[2]
第二个性质其实很容易证明,两个菲波那契数列相加只不过是修改了前两项的值,故不再赘述,有了这两个性质,就很方便来维护菲波那契数列的一些的操作了。
- 第一性质证明如下:
#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 " , query ( 1 , l , r ) ); } } }