E DZY Loves Fibonacci Numbers
In mathematical terms, the sequence Fn of Fibonacci numbers is defined by the recurrence relation
F1?=?1; F2?=?1; Fn?=?Fn?-?1?+?Fn?-?2 (n?>?2).
DZY loves Fibonacci numbers very much. Today DZY gives you an array consisting of nintegers: a1,?a2,?...,?an. Moreover, there are m queries, each query has one of the two types:
- Format of the query "1 l r". In reply to the query, you need to add Fi?-?l?+?1 to each element ai, where l?≤?i?≤?r.
- Format of the query "2 l r". In reply to the query you should output the value of modulo 1000000009 (109?+?9).
Help DZY reply to all the queries.
Input
The first line of the input contains two integers n and m (1?≤?n,?m?≤?300000). The second line contains n integers a1,?a2,?...,?an (1?≤?ai?≤?109) — initial array a.
Then, m lines follow. A single line describes a single query in the format given in the statement. It is guaranteed that for each query inequality 1?≤?l?≤?r?≤?n holds.
Output
For each query of the second type, print the value of the sum on a single line.
Example
Input
4 41 2 3 41 1 42 1 41 2 42 1 3
Output
1712
Note
After the first query, a?=?[2,?3,?5,?7].
For the second query, sum?=?2?+?3?+?5?+?7?=?17.
After the third query, a?=?[2,?4,?6,?9].
For the fourth query, sum?=?2?+?4?+?6?=?12.
题目的大意就是,定义了fib数列前两项F[1]=1,F[2]=1。给出整数n,m,再给出n个数和m项操作。
每个操作包含三个数op,l,r,若op=1,就在[l,r]的区间相应加上F[1,r-l+1](每个数对应着加),若op=2,就将[l,r]中所有数统计和并输出。
首先,我们要知道,fib数列有个特殊的性质_两个fib数列相加,还是fib数列,只是前两项的值变动了而已(从而导致后面的数变动).
那么,根据这个性质,就可以通过累计的方法实现_用线段树进行维护.
首先,我们需要知道一些性质:
设fib[1]=a,fib[2]=b,则fib[n]=fib[n-1]*b+fib[n-2]*a.(可推)
fib[1]+fib[2]+fib[3]+......+fib[n]=fib[n+2]-fib[2].(可推)
通过这两个性质,我们可以巧妙的记录线段树上每一个节点的前两个(fib[1],fib[2])的值,然后计算出这个节点上[L,R],所有数的和.
怎么来?
我们对于每一个线段树上的节点,都记录两个值,ta,tb,分别表示这个区间前两项的值,在更新时,
1 void upt(int now,int fir,int sec,int len){ 2 T[now].ta=(T[now].ta+fir)%TT,T[now].tb=(T[now].tb+sec)%TT; 3 T[now].key=((long long)T[now].key+(long long)fir*fib[len]+(long long)sec*fib[len+1]-(long long)sec)%TT; 4 }
其中前两句是将标记累计,最后以句就是顺带计算出这个节点的值.
同时,在pushdown函数中,也要对此进行更新.
1 void push_down(int now){ 2 if (!T[now].ta&&!T[now].tb) return; 3 int L=T[now].L,R=T[now].R; 4 upt(now<<1,T[now].ta,T[now].tb,mid-L+1); 5 int len=mid-L+1,a,b; 6 a=((long long)T[now].ta*fib[len-1]+(long long)T[now].tb*fib[len])%TT; 7 b=((long long)T[now].ta*fib[len]+(long long)T[now].tb*fib[len+1])%TT; 8 upt(now<<1|1,a,b,R-mid); 9 T[now].ta=T[now].tb=0; 10 }
其中,对于某个节点的子节点,有两段.一段[L,mid],一段[mid+1,R].
那么更新[L,mid]时,它的前缀和当前节点是一样的,所以不需改动信息,直接下传.
更新[mid+1,R]时,需要重新计算出这段区间前两项的值(这可以用上面说的性质算),然后才能下传.
最后下传完了记得把标记清空.
不过不知道为什么,我的线段树开4,5倍空间会炸,开10倍才不炸,很玄学......
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define mid ((L+R)>>1) 5 using namespace std; 6 const int TT=1000000009,maxn=300007; 7 int n,m,a[maxn],fib[maxn]; 8 struct ST{int L,R,key,ta,tb;}T[maxn*10]; 9 int read(){ 10 int x=0; char ch=getchar(); 11 while (ch<‘0‘||ch>‘9‘) ch=getchar(); 12 while (ch>=‘0‘&&ch<=‘9‘) x=x*10+ch-‘0‘,ch=getchar(); 13 return x; 14 } 15 void make(int now,int L,int R){ 16 T[now].L=L,T[now].R=R; 17 if (L==R){T[now].key=a[L]; return;} 18 make(now<<1,L,mid),make(now<<1|1,mid+1,R); 19 T[now].key=(T[now<<1].key+T[now<<1|1].key)%TT; 20 } 21 void upt(int now,int fir,int sec,int len){ 22 T[now].ta=(T[now].ta+fir)%TT,T[now].tb=(T[now].tb+sec)%TT; 23 T[now].key=((long long)T[now].key+(long long)fir*fib[len]+(long long)sec*fib[len+1]-(long long)sec)%TT; 24 } 25 void push_down(int now){ 26 if (!T[now].ta&&!T[now].tb) return; 27 int L=T[now].L,R=T[now].R; 28 upt(now<<1,T[now].ta,T[now].tb,mid-L+1); 29 int len=mid-L+1,a,b; 30 a=((long long)T[now].ta*fib[len-1]+(long long)T[now].tb*fib[len])%TT; 31 b=((long long)T[now].ta*fib[len]+(long long)T[now].tb*fib[len+1])%TT; 32 upt(now<<1|1,a,b,R-mid); 33 T[now].ta=T[now].tb=0; 34 } 35 void alter(int now,int aimL,int aimR){ 36 int L=T[now].L,R=T[now].R; 37 if (L>aimR||R<aimL) return; 38 if (L>=aimL&&R<=aimR){upt(now,fib[L-aimL+1],fib[L-aimL+2],R-L+1); push_down(now); return;} 39 push_down(now); 40 alter(now<<1,aimL,aimR),alter(now<<1|1,aimL,aimR); 41 T[now].key=(T[now<<1].key+T[now<<1|1].key)%TT; 42 } 43 int seek(int now,int aimL,int aimR){ 44 int L=T[now].L,R=T[now].R; 45 if (L>aimR||R<aimL) return 0; 46 if (L>=aimL&&R<=aimR) return T[now].key; 47 push_down(now); 48 return (seek(now<<1,aimL,aimR)+seek(now<<1|1,aimL,aimR))%TT; 49 } 50 int main(){ 51 n=read(),m=read(),memset(T,0,sizeof T); 52 for (int i=1; i<=n; i++) a[i]=read(); 53 make(1,1,n); 54 fib[0]=0,fib[1]=fib[2]=1; 55 for (int i=3; i<=n+3; i++) fib[i]=(fib[i-1]+fib[i-2])%TT; 56 for (int i=1; i<=m; i++){ 57 int tp=read(),L=read(),R=read(); 58 if (tp==1) alter(1,L,R); 59 else printf("%d\n",seek(1,L,R)); 60 } 61 return 0; 62 }