題目大意
有一個斐波那契數列: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<cstdio>
#include<cstring>
#include<algorithm>
#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;
}