CF446C DZY Loves Fibonacci Numbers 线段树 + 数学
有两个性质需要知道:
$1.$ 对于任意的 $f[i]=f[i-1]+f[i-2]$ 的数列,都有 $f[i]=fib[i-2]\times f[1]+fib[i-1]\times f[2]$
其中 $fib[i]$ 为第 $i$ 项斐波那契数列.
$2$. 对于任意满足上述条件的数列,都有 $\sum_{i=1}^{n}f[i]=f[n+2]-f[2]$
$3.$ 任意两断满足上述条件的数列每一项依次叠加,依然满足 $g[i]=g[i-1]+g[i-2]$,且上述两个性质都满足.
$4.$ 任何一段斐波那契数列也满足上述所有性质.
有了上述预备知识后,再考虑这道题:
我们用线段树来维护区间和,线段树上每个节点维护 $3$ 个信息,为 $sum,f1,f2$
即节点所维护的区间和,以及该节点及线段树中区间要加上一个前两项为 $f1,f2$ 的上述递推数列.
那么,我们只需考虑如何下传标记,如何查询即可.
假设当前节点已经有了 $f1,f2$,那么将标记下传给左子树是轻松的:直接下传即可,区间和的贡献可按照上述公式 $O(1)$ 求出.
而如果要下传给右儿子的话就不能直接传了,因为右儿子区间开头的两项并不是 $f1,f2$.
而根据上述三条性质,我们知道斐波那契数列的任何一段也是斐波那契数列.
所以,直接算出右儿子的 $f1,f2$ 即 $f1\times fib[mid-l]+f2\times fib[mid-l+1]$ 与 $f1\times fib[mid-l+1]+f2\times fib[mid-l+2]$
然后还知道 $f1,f2$ 都满足叠加性,所以直接叠加到左右儿子的 $f1,f2$ 上即可.
#include <bits/stdc++.h> #define N 400004 #define LL long long #define lson now<<1 #define rson now<<1|1 #define setIO(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout) using namespace std; const LL mod=1000000009; int n,m; LL fib[N<<1],sum[N<<1]; struct node { LL f1,f2,sum; int l,r,len; }t[N<<2]; void build(int l,int r,int now) { t[now].l=l; t[now].r=r; t[now].len=r-l+1; if(l==r) return ; int mid=(l+r)>>1; if(l<=mid) build(l,mid,lson); if(r>mid) build(mid+1,r,rson); } void mark(int now,LL f1,LL f2) { (t[now].f1+=f1)%=mod; (t[now].f2+=f2)%=mod; (t[now].sum+=f1*fib[t[now].len]%mod+f2*fib[t[now].len+1]%mod-f2+mod)%=mod; } void pushup(int now) { t[now].sum=(t[lson].sum+t[rson].sum)%mod; } void pushdown(int now) { if(t[now].f1==0&&t[now].f2==0) return; int mid=(t[now].l+t[now].r)>>1; mark(lson,t[now].f1,t[now].f2); if(t[now].r>mid) mark(rson,t[now].f1*fib[t[lson].len-1]%mod+t[now].f2*fib[t[lson].len]%mod,t[now].f1*fib[t[lson].len]%mod+t[now].f2*fib[t[lson].len+1]%mod); t[now].f1=t[now].f2=0; } void update(int l,int r,int now,int L,int R) { if(l>=L&&r<=R) { mark(now,fib[l-L+1],fib[l-L+2]); return; } pushdown(now); int mid=(l+r)>>1; if(L<=mid) update(l,mid,lson,L,R); if(R>mid) update(mid+1,r,rson,L,R); pushup(now); } LL query(int l,int r,int now,int L,int R) { if(l>=L&&r<=R) { return t[now].sum; } pushdown(now); int mid=(l+r)>>1; LL re=0ll; if(L<=mid) re+=query(l,mid,lson,L,R); if(R>mid) re+=query(mid+1,r,rson,L,R); return re%mod; } int main() { // setIO("input"); int i,j; scanf("%d%d",&n,&m); fib[1]=fib[2]=1; for(i=3;i<N;++i) fib[i]=(fib[i-1]+fib[i-2])%mod; for(i=1;i<=n;++i) scanf("%lld",&sum[i]), (sum[i]+=sum[i-1])%=mod; build(1,n,1); for(i=1;i<=m;++i) { int opt,l,r; scanf("%d%d%d",&opt,&l,&r); if(opt==1) update(1,n,1,l,r); else printf("%lld\n",(query(1,n,1,l,r)+sum[r]-sum[l-1]+mod*2)%mod); } return 0; }