题意
给一个数列,支持区间修改和区间求和,区间修改操作给区间第(i)位加上(fib_i)
思路
如果使用线段树的话,显然区间和可以合并,考虑两个问题:
-
如何化区间修改为快速的打标记
-
打标记之后如何快速计算这个标记的贡献
令(f)表示斐波那契数列,定义广义斐波那契数列:(g_i=a imes f_{i-1} + b imes f_{i-2},(igeq 3),g_1=a,g_2=b),显然,对于(g)有(g_i=g_{i-1}+g_{i-2})(乘法分配律)
定理:$$sum_{i=1}^n{g_i} = g_{n+2} - g_2$$
由数学归纳法容易证明:(n=1)显然成立,(ngeq 2)时,有$$sum_{i=1}^n{g_i} = sum_{i=1}^{n-1}{g_i} + g_n = (g_{n+1}-g_2) + g_n = g_{n+2} -g_2$$
而根据(g)的定义,有$$g_{n+2} - g_2 = a imes f_n + b imes f_{n+1} - b$$
显然一个斐波那契数列加上另一个斐波那契数列仍然是斐波那契数列(广义),由上式可以知道,区间和只与(a,b,n)有关,由于(a,b)即(g_1,g_2),(n)为区间长度,所以只用知道区间的前两项就足够求出区间的和啦
对于一个区间操作,只用给前两位打标记,求贡献用上面的公式即可;下传标记左区间直接下传,右区间算一下再下传
P.S. 不用担心区间长度为1的情况,(g_1 = g_3 - g_2)依然成立
Code
#include<bits/stdc++.h>
#define N 600005
#define Min(x,y) ((x)<(y)?(x):(y))
#define Max(x,y) ((x)>(y)?(x):(y))
using namespace std;
typedef long long ll;
const ll mod = 1000000009;
int n,m,a[N];
int k,L,R;
ll sum[N<<2],f[N+100],tag1[N<<2],tag2[N<<2];
template <class T>
void read(T &x)
{
char c; int sign=1;
while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
while((c=getchar())>='0'&&c<='9') x=(x<<1)+(x<<3)+c-48; x*=sign;
}
void pushup(int rt) { sum[rt]=(sum[rt<<1]+sum[rt<<1|1])%mod; }
void add_sign(int rt,int l,int r,ll a,ll b)
{
tag1[rt]=(tag1[rt]+a)%mod;
tag2[rt]=(tag2[rt]+b)%mod;
int len=(r-l+1);
sum[rt] = (sum[rt] + a*f[len]%mod + b*f[len+1]%mod - b)%mod;
}
void pushdown(int rt,int l,int r)
{
if(!tag1[rt]&&!tag2[rt]) return;
int mid=(l+r)>>1;
int Llen=(mid-l+1);
ll rig1=(tag1[rt]*f[Llen-1]%mod + tag2[rt]*f[Llen]%mod)%mod;
ll rig2=(tag1[rt]*f[Llen]%mod + tag2[rt]*f[Llen+1]%mod)%mod;
add_sign(rt<<1,l,mid,tag1[rt],tag2[rt]);
add_sign(rt<<1|1,mid+1,r,rig1,rig2);
tag1[rt]=tag2[rt]=0;
}
void modify(int rt,int l,int r,int x,int y)
{
if(x<=l&&r<=y) return add_sign(rt,l,r,f[l-L+1],f[l-L+2]);
int mid=(l+r)>>1;
pushdown(rt,l,r);
if(x<=mid) modify(rt<<1,l,mid,x,y);
if(y>mid) modify(rt<<1|1,mid+1,r,x,y);
pushup(rt);
}
ll query(int rt,int l,int r,int x,int y)
{
if(x<=l&&r<=y) return sum[rt];
int mid=(l+r)>>1;
pushdown(rt,l,r);
ll ret=0;
if(x<=mid) ret=(ret+query(rt<<1,l,mid,x,y))%mod;
if(y>mid) ret=(ret+query(rt<<1|1,mid+1,r,x,y))%mod;
return ret;
}
void build(int rt,int l,int r)
{
if(l==r) { sum[rt]=a[l]; return; }
int mid=(l+r)>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
pushup(rt);
}
void init()
{
f[1]=f[2]=1;
for(int i=3;i<=n+10;++i) f[i]=(f[i-1]+f[i-2])%mod;
}
int main()
{
read(n);read(m);init();
for(int i=1;i<=n;++i) read(a[i]);
build(1,1,n);
while(m--)
{
read(k);read(L);read(R);
if(k==2) printf("%lld
",(query(1,1,n,L,R)%mod+mod)%mod);
else modify(1,1,n,L,R);
}
return 0;
}