Description:
给出一个数列,每次可以选取一个区间,按顺序加上第i个Fibonacci Numbers(斐波那契数)进行更新,也可以查询某一个区间的总和。
Hint:
(n le 3*10^5)
Solution:
数据结构结合数学
首先有公式 (sum _{i=1}^n fib(i)=fib(n+2))
且由于斐波那契的递推是线性的,故所有的(fib(i))都可以表示成(a*fib(1)+b*fib(2))的形式
考虑用线段树维护一个区间前两个位置的系数,然后就可以做了
#include <map>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define ls p<<1
#define rs p<<1|1
using namespace std;
typedef long long ll;
const ll mxn=1e6+5,mod=1e9+9;
ll n,m,cnt,a[mxn],hd[mxn];
ll f[mxn],tr[mxn<<2],vis[mxn<<2],tag[mxn<<2][2];
inline ll read() {
char c=getchar(); ll x=0,f=1;
while(c>'9'||c<'0') {if(c=='-') f=-1;c=getchar();}
while(c<='9'&&c>='0') {x=(x<<3)+(x<<1)+(c&15);c=getchar();}
return x*f;
}
inline void chkmax(ll &x,ll y) {if(x<y) x=y;}
inline void chkmin(ll &x,ll y) {if(x>y) x=y;}
struct ed {
ll to,nxt;
}t[mxn<<1];
inline void add(ll u,ll v) {
t[++cnt]=(ed) {v,hd[u]}; hd[u]=cnt;
}
void push_up(ll p) {
tr[p]=(tr[ls]+tr[rs])%mod;
}
struct ST {
ll x,y;
};
ST fibget(ST tp,ll len) {
ll c=tp.x*f[len]%mod+tp.y*f[len+1]%mod,d=tp.x*f[len+1]%mod+tp.y*f[len+2]%mod;
return (ST) {c%mod,d%mod};
}
ll fibcal(ll f1,ll f2,ll len) {
ll res=0;
if(len>=1) res=f1;
if(len>=2) res=(res+f2)%mod;
if(len>=3) res=(res+f2*(f[len+1]-2)%mod+f1*(f[len]-1)%mod+mod)%mod;
//前两项的系数的值,直接乘以当前f[i+1],f[i],计算出f[i+2]
return res;
}
void push_down(ll p,ll l,ll r) {
ll mid=(l+r)>>1;
if(vis[p]) {
ll &a=tag[p][0],&b=tag[p][1];
tag[ls][0]=(tag[ls][0]+a)%mod;
tag[ls][1]=(tag[ls][1]+b)%mod;
tr[ls]=(tr[ls]+fibcal(a,b,mid-l+1))%mod;
vis[ls]=1;
ll c=a*f[mid-l]+b*f[mid-l+1],d=a*f[mid-l+1]+b*f[mid-l+2];
c%=mod; d%=mod;
tag[rs][0]=(tag[rs][0]+c)%mod;
tag[rs][1]=(tag[rs][1]+d)%mod;
tr[rs]=(tr[rs]+fibcal(c,d,r-mid))%mod;
vis[rs]=1;
vis[p]=tag[p][0]=tag[p][1]=0;
}
}
void build(ll l,ll r,ll p) {
if(l==r) {tr[p]=a[l]; return ;}
ll mid=(l+r)>>1;
build(l,mid,ls); build(mid+1,r,rs); push_up(p);
}
void modify(ll l,ll r,ll ql,ll qr,ll val1,ll val2,ll p) {
if(ql<=l&&r<=qr) {
tag[p][0]=(tag[p][0]+val1)%mod;
tag[p][1]=(tag[p][1]+val2)%mod;
tr[p]=(tr[p]+fibcal(val1,val2,r-l+1))%mod;
vis[p]=1; return ;
}
ll mid=(l+r)>>1; push_down(p,l,r);
if(qr<=mid) modify(l,mid,ql,qr,val1,val2,ls);
else if(ql>mid) modify(mid+1,r,ql,qr,val1,val2,rs);
else {
modify(l,mid,ql,qr,val1,val2,ls);
ST z=fibget((ST){val1,val2},mid-ql);
modify(mid+1,r,mid+1/*询问区间也要更改*/,qr,z.x,z.y,rs); //算出区间前两个fib,貌似也可以用前缀和
}
push_up(p);
}
ll query(ll l,ll r,ll ql,ll qr,ll p) {
if(ql<=l&&r<=qr) return tr[p];
ll mid=(l+r)>>1; push_down(p,l,r); ll res=0;
if(ql<=mid) res=(res+query(l,mid,ql,qr,ls))%mod;
if(qr>mid) res=(res+query(mid+1,r,ql,qr,rs))%mod;
return res;
}
void init() {
n=read(); m=read(); ll x,y,opt; f[1]=f[2]=1;
for(ll i=3;i<=n+4;++i) f[i]=(f[i-1]+f[i-2])%mod;
for(ll i=1;i<=n;++i) a[i]=read()%mod; build(1,n,1);
for(ll i=1;i<=m;++i) {
opt=read(); x=read(); y=read();
if(opt==1) modify(1,n,x,y,1,1,1);
else printf("%lld
",(query(1,n,x,y,1)+mod)%mod);
}
}
int main()
{
init();
return 0;
}