CF446C(线段树+斐波那契)
CF446C(线段树+斐波那契数列)
题目大意:
区间加斐波那契数列,区间求和
分析:
一眼鉴定为线段树
难点在于如何打标记,合并和传递标记
对于斐波那契数列有几个性质(下方会给出解释):
性质1:
两个长度相等的斐波那契数列相加之后仍然可以满足斐波那契 \(f_i=f_{i-1}+f_{i-2}\) 的性质(仍然是一个广义斐波那契数列)。
性质2:
通过维护每个斐波那契数列的第一第二项就可以 \(O(1)\) 的获得任意一项的数值和任意 \(i\) 项的前缀和.
对于性质1:
假设有两个斐波那契数列相加
\(1,2,3,5\)
\(13,21,34,55\)
结果为
\(14,23,37,60\)
其任然是一个广义斐波那契数列
对于性质2:
将斐波那契数列每一项进行拆解
\(f_1=f_1\)
\(f_2=f_2\)
\(f_3=f_2+f_1\)
\(f_4=f_3+f_2=2*f_2+f_1\)
\(……\)
\(f_i=f_{i-1}*f_2+f_{i-2}*f_1\)
斐波那契数列每一项都能用第一第二项来表示。
对于任意 \(i\) 的前缀和
设 \(s_1=a,s_2=b\)
有 \(s_i=a*s_{i-2}+b*s_{i-1}\)
维护标记:
维护某一段区间的和,我们可以在线段树每个节点上维护两个数 \(a,b\) 表示第一个数加了多少,第二个数加了多少。求区间和时,只需要套用上面的式子。
代码:
#include<bits/stdc++.h>
#define int long long
#define MAXN 300860
#define mod 1000000009
using namespace std;
struct T{
int l,r,node,len,lazy,val;
}t[MAXN<<2];
int n,m,a[MAXN],f[MAXN],op;
void updata(int node){
t[node].val=(t[node<<1].val+t[node<<1|1].val)%mod;
}
void build(int l,int r,int node){
if(l==r){
t[node].val=a[l];
return ;
}
int mid=(l+r)>>1;
build(l,mid,node<<1);
build(mid+1,r,node<<1|1);
updata(node);
}
int cal(int a,int b,int k){
if(k==1)return a;
if(k==2)return b;
return (a*f[k-2]%mod+b*f[k-1]%mod)%mod;
}
int getsum(int a,int b,int k){
if(k==1)return a;
if(k==2)return(a+b)%mod;
return (cal(a,b,k+2)-b+mod)%mod;
}
void paint(int node,int l,int r, int aa,int bb){
t[node].l=(t[node].l+aa)%mod;
t[node].r=(t[node].r+bb)%mod;
t[node].val+=getsum(aa,bb,r-l+1);
t[node].val%=mod;
}
void pushdown(int node,int l,int r,int mid){
if(!t[node].l)return ;
paint(node<<1,l,mid,t[node].l,t[node].r);
int aa=cal(t[node].l,t[node].r,mid+1-l+1);
int bb=cal(t[node].l,t[node].r,mid+2-l+1);
paint(node<<1|1,mid+1,r,aa,bb);
t[node].l=0;
t[node].r=0;
}
void change(int l,int r,int node,int x,int y){
if(x<=l&&r<=y){
paint(node,l,r,f[l-x+1],f[l-x+2]);
return ;
}
int mid=(l+r)>>1;
pushdown(node,l,r,mid);
if(x<=mid)change(l,mid,node<<1,x,y);
if(mid<y)change(mid+1,r,node<<1|1,x,y);
updata(node);
}
int ask(int l,int r,int node,int x,int y){
if(x<=l&&r<=y){
return t[node].val;
}
int mid=(l+r)>>1;
int ans=0;
pushdown(node,l,r,mid);
if(x<=mid)ans+=ask(l,mid,node<<1,x,y);
if(y>mid)ans+=ask(mid+1,r,node<<1|1,x,y);
return ans%mod;
}
signed main(){
std::ios::sync_with_stdio(false);
std::cin.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
}
build(1,n,1);
int x,y,k;
f[1]=1;
f[2]=1;
for(int i=3;i<=n+9;i++){//求斐波那契数列
f[i]=(f[i-1]+f[i-2])%mod;
}
for(int i=1;i<=m;i++){
cin>>op>>x>>y;
if(op==1){
change(1,n,1,x,y);
}
else{
cout<<ask(1,n,1,x,y)<<endl;
}
}
return 0;
}