部分前置知识
线段树维护区间等比数列加(比值固定):
对于每个区间的懒标记,只需维护区间加的首项即可,由于比值固定,因此懒标记的下传和合并都很简单,下传就是左区间不动,右区间把右区间的首项算出来传下去;合并就直接相加即可。下传时计算当前区间的值只需要用等比数列求和公式往区间上加就行了。
代码(待补充):
维护斐波那契加与区间和
题目:CF446C
方法1:
由于斐波那契数列有如下通项公式
F
n
=
1
5
[
(
1
+
5
2
)
n
−
(
1
−
5
2
)
n
]
F_n=\frac{1}{\sqrt{5}}[(\frac{1+\sqrt{5}}{2})^n\;-\;(\frac{1-\sqrt{5}}{2})^n]
Fn=51[(21+5)n−(21−5)n]
且
5
5
5 在该题模数
1
e
9
+
9
1e9+9
1e9+9 下二次剩余,因此在该模数意义下我们可以用整数表达出
5
\sqrt{5}
5 ,那么问题转化为两个等比数列加,同时维护两个懒标记即可。
若在模数下没有二次剩余,例如
1
e
9
+
7
1e9+7
1e9+7 ,则需要扩域,搞一个
a
+
b
5
a+b\sqrt{5}
a+b5 的类型重载下运算符。
参考:在模数下二次剩余的情况 / 需要扩域的情况
方法2:
需要一些广义Fibonacci数列的性质
设广义Fibonacci数列
F
F
F ,其中
F
1
=
a
,
F
2
=
b
F_1=a,F_2=b
F1=a,F2=b
设狭义Fibonacci数列
f
f
f ,其中
f
1
=
1
,
f
2
=
1
f_1=1,f_2=1
f1=1,f2=1
则容易推导出第一个性质:
F
n
=
a
∗
f
n
−
2
+
b
∗
f
n
−
1
F_n=a*f_{n-2}+b*f_{n-1}
Fn=a∗fn−2+b∗fn−1 (自己手玩几组或者归纳法都可以)
第二个性质:
∑
i
=
1
n
F
i
=
F
n
+
2
−
F
2
\sum_{i=1}^{n}F_i\;=\;F_{n+2}-F_2
∑i=1nFi=Fn+2−F2 ,证明可以由归纳法简单得到。
于是,
∑
i
=
1
n
F
i
=
a
∗
f
n
+
b
∗
f
n
+
1
−
b
\sum_{i=1}^{n}F_i\;=\;a*f_n+b*f_{n+1}-b
∑i=1nFi=a∗fn+b∗fn+1−b
这个式子只需要两个懒标记维护
a
,
b
a,b
a,b 即可用线段树维护,下传到右区间时依然是需要先算一算再传。
维护区间加与区间斐波那契和
题目:CF718C
区间斐波那契和这个东西可以用矩阵维护。
建树时直接矩阵快速幂把每个
f
a
i
,
f
a
i
−
1
f_{a_i}\;,\;f_{a_i-1}
fai,fai−1搞成叶子节点的矩阵,然后向上相加合并即可。
对于区间加的操作,在斐波那契和中的体现就是,对于每一个点,
f
a
i
+
w
=
f
a
i
∗
B
a
s
e
w
f_{a_i+w}=f_{a_i}*Base^w
fai+w=fai∗Basew,其中
B
a
s
e
Base
Base 是斐波那契数列的转移矩阵,因此区间加就转化为了矩阵的区间乘。
Code:
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#include<iomanip>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<bitset>
#define int long long
#define lowbit(x) x&(-x)
#define mp make_pair
#define rep(i,x,n) for(int i=x;i<=n;i++)
using namespace std;
const int mod=1e9+7;
const int maxn=1e5+5;
inline int read()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9')
{
if(c=='-') f=-1;
c=getchar();
}
while(c>='0'&&c<='9')
{
x=x*10+(c-'0');
c=getchar();
}
return x*f;
}
struct matrix{
int a[2][2];
inline void init()
{
a[0][0]=a[1][1]=1;a[0][1]=a[1][0]=0;
}
inline void clear()
{
a[0][0]=a[1][1]=a[0][1]=a[1][0]=0;
}
inline matrix operator * (const matrix &x)
{
matrix res;res.clear();
for(int i=0;i<=1;i++)
{
for(int j=0;j<=1;j++)
{
for(int k=0;k<=1;k++)
{
res.a[i][j]+=(a[i][k]*x.a[k][j])%mod;
res.a[i][j]%=mod;
}
}
}
return res;
}
inline matrix operator + (const matrix &x)
{
matrix res;
for(int i=0;i<=1;i++) for(int j=0;j<=1;j++) res.a[i][j]=(a[i][j]+x.a[i][j])%mod;
return res;
}
}G,B,C;
struct node{
int l,r;
matrix pre,tag;
bool vis;
}t[maxn<<2];
inline matrix quickpow(matrix x,int tim)
{
matrix res;res.init();
while(tim)
{
if(tim&1) res=res*x;
x=x*x;
tim>>=1;
}
return res;
}
int a[maxn],n,m;
inline void build(int p,int l,int r)
{
t[p].l=l;t[p].r=r;t[p].tag.init();t[p].vis=0;
if(l==r)
{
if(a[l]>=2) t[p].pre=B*quickpow(G,a[l]-2);
else t[p].pre=C;
return;
}
int mid=(l+r)/2;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
t[p].pre=t[p*2].pre+t[p*2+1].pre;
}
inline void spread(int p)
{
if(t[p].vis)
{
t[p*2].pre=t[p*2].pre*t[p].tag;t[p*2+1].pre=t[p*2+1].pre*t[p].tag;
t[p*2].tag=t[p*2].tag*t[p].tag;t[p*2+1].tag=t[p*2+1].tag*t[p].tag;
t[p*2].vis=t[p*2+1].vis=1;t[p].vis=0;t[p].tag.init();
}
}
inline void change(int p,int l,int r,matrix v)
{
if(t[p].l>=l&&t[p].r<=r)
{
t[p].tag=t[p].tag*v,t[p].pre=t[p].pre*v,t[p].vis=1;
return;
}
spread(p);
int mid=(t[p].l+t[p].r)/2;
if(l<=mid) change(p*2,l,r,v);
if(r>mid) change(p*2+1,l,r,v);
t[p].pre=t[p*2].pre+t[p*2+1].pre;
}
inline int ask(int p,int l,int r)
{
if(t[p].l>=l&&t[p].r<=r)
{
return t[p].pre.a[0][0]%mod;
}
spread(p);
int mid=(t[p].l+t[p].r)/2;
int res=0;
if(l<=mid) res+=ask(p*2,l,r);
res%=mod;
if(r>mid) res+=ask(p*2+1,l,r);
return res%mod;
}
signed main()
{
C.a[0][0]=1;C.a[0][1]=C.a[1][0]=C.a[1][1]=0;
B.a[0][0]=B.a[0][1]=1;B.a[1][0]=B.a[1][1]=0;
G.a[0][0]=G.a[0][1]=G.a[1][0]=1;G.a[1][1]=0;
n=read();m=read();
rep(i,1,n) a[i]=read();
build(1,1,n);
rep(i,1,m)
{
int opt=read();
if(opt==1)
{
int x=read(),y=read(),z=read();
matrix tmp=quickpow(G,z);
change(1,x,y,tmp);
}
else
{
int x=read(),y=read();
printf("%lld\n",ask(1,x,y));
}
}
}
一个莫队+线段树+斐波那契题:CF633H