有一个斐波那契数列:F(1)=1;F(2)=1;F(n)=F(n-1)+F(n-2)(n>2);
也有一个序列长度为n的A;
还有两种操作:
1、“1 L r”,表示给ai 加上F(i-L+1) ,其中L<=i<=r ;
2、“2 L r”,表示询问 的值。
n,m<=100000,a[i]<=1e9
时间限制4s
空间限制256M
这题做得我满脑子都是尼克杨的问号,一定要写一写。
如果区间修改的不是斐波那契数的话,就是很简单的线段树;而现在就要通过特别的方式来使用线段树。
这棵线段树的tag是一个二元组(a,b),表示要加上去的斐波那契数列的前两项,至于怎么用前两项和区间长度快速求和呢,来看:
斐波那契第二项之后的数都是由前两个数乘不同的系数得来的,所以我们可以先得出这两个系数,就是说比如f[i]=f[1]*a+f[2]*b,我们先求a和b。
f[1]=1*f[1]+0*f[2];
f[2]=0*f[1]+1*f[2];
f[3]=1*f[1]+1*f[2];
f[4]=1*f[1]+2*f[2];
这样推一下就会发现,f[1]和f[2]的系数也可以构成斐波那契数列,递推预处理一下即可,对a和b分别做前缀和,就可以O(1)知道一个区间内有多少个f[1]和f[2];
至于线段树的实现细节,我们来看一条结论:
对于类斐波那契数列H1=a, H2=b,满足:
1、 Hn = a ∗ Fn−2 + b ∗ Fn−1
2、H1 + H2 + … + Hn = Hn+2 − b
用这个结合之前的前缀和可以直接求出H中的任意一项。
这一条在Pushdown的时候用得到。
最后贴代码
#include
#include
#include
#define maxn 100006
#define fr(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const ll ding=1e9+9;
struct nod
{
ll a,b;
} c[maxn*4];
int i,n,m,z,l,r,a[maxn];
ll s,f[3][maxn],pr[3][maxn],tr[maxn*4];
void pre(int n)
{
f[1][1]=1,f[1][2]=0;
f[2][1]=0,f[2][2]=1;
pr[1][1]=1,pr[1][2]=1;
pr[2][1]=0,pr[2][2]=1;
int i,j;
fr(i,3,n)
{
fr(j,1,2)
{
f[j][i]=(f[j][i-1]+f[j][i-2])%ding;
pr[j][i]=(pr[j][i-1]+f[j][i])%ding;
}
}
return;
}
void maket(int v,int l,int r)
{
if (l==r)
{
tr[v]=a[l];
return;
}
int m=(l+r) >> 1;
maket(v+v,l,m);
maket(v+v+1,m+1,r);
tr[v]=(tr[v+v]+tr[v+v+1])%ding;
return;
}
ll calc(ll a,ll b,int l)
{
if (l==1) return a;
return (pr[1][l]*a%ding+pr[2][l]*b%ding)%ding;
}
ll get(ll a,ll b,ll n)
{
if (!n) return 0;
if (n==1) return a;
if (n==2) return b;
if (n==3) return a+b;
return (calc(a,b,n-2)+b)%ding;
}
void update(int v,int st,int en)
{
if (!c[v].a && !c[v].b) return;
ll a=c[v].a,b=c[v].b;
int m=(st+en) >> 1;
tr[v+v]=(tr[v+v]+calc(a,b,m-st+1))%ding;
ll a_=get(a,b,m-st+2),b_=get(a,b,m-st+3);
tr[v+v+1]=(tr[v+v+1]+calc(a_,b_,en-m))%ding;
c[v+v].a=(c[v+v].a+a)%ding;
c[v+v].b=(c[v+v].b+b)%ding;
c[v+v+1].a=(c[v+v+1].a+a_)%ding;
c[v+v+1].b=(c[v+v+1].b+b_)%ding;
c[v].a=c[v].b=0;
return;
}
void modify(int v,int st,int en,int l,int r,nod x)
{
if (st==l && en==r)
{
ll tt;
tt=calc(x.a,x.b,en-st+1);
tr[v]=(tr[v]+tt)%ding;
c[v].a=(c[v].a+x.a)%ding;
c[v].b=(c[v].b+x.b)%ding;
return;
}
update(v,st,en);
int m=(st+en) >> 1;
if (r<=m) modify(v+v,st,m,l,r,x);
else if (l>m) modify(v+v+1,m+1,en,l,r,x);
else
{
nod vv;
if (m-l+1>=2)
{
vv.a=get(x.a,x.b,m-l+2);
vv.b=get(x.a,x.b,m-l+3);
} else vv.a=x.b,vv.b=x.a+x.b;
modify(v+v,st,m,l,m,x);
modify(v+v+1,m+1,en,m+1,r,vv);
}
tr[v]=(tr[v+v]+tr[v+v+1])%ding;
return;
}
void findd(int v,int st,int en,int l,int r)
{
if (st==l && en==r)
{
s=(s+tr[v])%ding;
return;
}
update(v,st,en);
int m=(st+en) >> 1;
if (r<=m) findd(v+v,st,m,l,r);
else if (l>m) findd(v+v+1,m+1,en,l,r);
else
{
findd(v+v,st,m,l,m);
findd(v+v+1,m+1,en,m+1,r);
}
tr[v]=(tr[v+v]+tr[v+v+1])%ding;
return;
}
int main()
{
freopen("czgj3.in","r",stdin);
freopen("thi.out","w",stdout);
scanf("%d%d",&n,&m);
pre(n);
fr(i,1,n) scanf("%d",&a[i]);
maket(1,1,n);
fr(i,1,m)
{
scanf("%d%d%d",&z,&l,&r);
if (z==1)
{
nod t;
t.a=1,t.b=1;
modify(1,1,n,l,r,t);
} else
{
s=0;
findd(1,1,n,l,r);
printf("%lld\n",s);
}
}
return 0;
}