题目
思路
注意到 f 1 = f 2 = 1 f_1=f_2=1 f1=f2=1 ,可以把递推公式写得更有趣,
f n = f 2 f n − 1 + f 1 f n − 2 (1) f_{n}=f_{2}f_{n-1}+f_{1}f_{n-2}\tag{1} fn=f2fn−1+f1fn−2(1)
然后把 f n − 1 f_{n-1} fn−1 展开成 f n − 2 + f n − 3 f_{n-2}+f_{n-3} fn−2+fn−3 得到
f n = f 2 f n − 2 + f 2 f n − 3 + f 1 f n − 2 f_{n}=f_{2}f_{n-2}+f_2f_{n-3}+f_1f_{n-2} fn=f2fn−2+f2fn−3+f1fn−2
由于 f 1 + f 2 = f 3 f_1+f_2=f_3 f1+f2=f3 ,所以我们利用分配率得到
f n = f 3 f n − 2 + f 2 f n − 3 (2) f_n=f_3f_{n-2}+f_2f_{n-3}\tag{2} fn=f3fn−2+f2fn−3(2)
仔细观察, ( 1 ) (1) (1) 式和 ( 2 ) (2) (2) 式拥有相似的结构。不难想象,我们一定可以一直递推,得到恒等式
f n = f m f n − m + 1 + f m − 1 f n − m f_n=f_{m}f_{n-m+1}+f_{m-1}f_{n-m} fn=fmfn−m+1+fm−1fn−m
即
f n + m = f m f n + 1 + f m − 1 f n (3) f_{n+m}=f_mf_{n+1}+f_{m-1}f_n\tag{3} fn+m=fmfn+1+fm−1fn(3)
又因为 f m − 1 = f m + 1 − f m f_{m-1}=f_{m+1}-f_m fm−1=fm+1−fm ,进一步展开为
f n + m = f m f n + 1 + f m + 1 f n − f m f n f_{n+m}=f_mf_{n+1}+f_{m+1}f_n-f_{m}f_{n} fn+m=fmfn+1+fm+1fn−fmfn
把这个式子用到题目里去,就可以看出
∑ i = l r f a i + x = f x ∑ i = l r f a i + 1 + f x + 1 ∑ i = l r f a i − f x ∑ i = l r f a i \sum_{i=l}^{r}f_{a_i+x}=f_x\sum_{i=l}^{r}f_{a_i+1}+f_{x+1}\sum_{i=l}^{r}f_{a_i}-f_x\sum_{i=l}^{r}f_{a_i} i=l∑rfai+x=fxi=l∑rfai+1+fx+1i=l∑rfai−fxi=l∑rfai
所以我们只需要求区间的 ∑ f a i \sum f_{a_i} ∑fai 与 ∑ f a i + 1 \sum f_{a_i+1} ∑fai+1 。等等,后面这个怎么转移?
很简单,直接用 ( 3 ) (3) (3) 式的变体, f n + m + 1 = f m + 1 f n + 1 + f m f n f_{n+m+1}=f_{m+1}f_{n+1}+f_{m}f_{n} fn+m+1=fm+1fn+1+fmfn 即可。
剩下的问题就是快速计算 f n f_n fn 的值了。简单的做法是矩阵快速幂,然而我们有更快的做法。
众所周知 f n = 5 5 [ ( 1 + 5 2 ) n − ( 1 − 5 2 ) n ] f_n=\frac{\sqrt{5}}{5}\left[(\frac{1+\sqrt{5}}{2})^n-(\frac{1-\sqrt 5}{2})^n\right] fn=55[(21+5)n−(21−5)n] (这个可以用各种方法推)。我们可以考虑直接按照这个式子计算。然而 5 \sqrt{5} 5 在模 1 0 9 + 7 10^9+7 109+7 意义下不存在,所以我们手写一个 a + b 5 a+b\sqrt 5 a+b5 类型,重载加减乘法运算,类似虚数。
另一点是快速幂还有 log \log log 的复杂度比较烦,我们使用光速幂,具体地,有 a b = a 32768 x + y = ( a 32768 ) x a y a^b=a^{32768x+y}=(a^{32768})^xa^y ab=a32768x+y=(a32768)xay ,预处理 a a a 和 a 32768 a^{32768} a32768 的 0 0 0 到 32767 32767 32767 次幂即可 O ( 1 ) \mathcal O(1) O(1) 求幂。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int LIM=32768,inv2=500000004,inv5=400000003,mod=1e9+7,N=2e6;
struct comp
{
int a,b;
comp operator +(const comp &t)const{return (comp){(a+t.a)%mod,(b+t.b)%mod};}
comp operator -(const comp &t)const{return (comp){(a-t.a)%mod,(b-t.b)%mod};}
comp operator *(const comp &t)const{return (comp){(1ll*a*t.a+5ll*b*t.b)%mod,(1ll*a*t.b+1ll*b*t.a)%mod};}
}pw0[2][LIM+100],pw1[2][LIM+100];
const comp F0={inv2,inv2},F1={inv2,mod-inv2};
struct Node
{
int s1,s2;
Node operator +(const Node &a)const{return (Node){(s1+a.s1)%mod,(s2+a.s2)%mod};}
Node operator *(const Node &a)const{return (Node){(1ll*s1*a.s2+1ll*(s2-s1)*a.s1)%mod,(1ll*s1*a.s1+1ll*s2*a.s2)%mod};}
}a[N],tag[N];
int w[N],n,m;
void make()
{
pw0[0][0]=pw1[0][0]=pw0[1][0]=pw1[1][0]=(comp){1,0};
for(int i=1;i<=LIM;i++)pw0[0][i]=pw0[0][i-1]*F0,pw0[1][i]=pw0[1][i-1]*F1;
comp t0=pw0[0][LIM],t1=pw0[1][LIM];
for(int i=1;i<=LIM;i++)pw1[0][i]=pw1[0][i-1]*t0,pw1[1][i]=pw1[1][i-1]*t1;
}
comp qpower(int n,int f){return pw1[f][(n>>15)&32767]*pw0[f][n&32767];}
int F(int n){return ((comp){0,inv5}*(qpower(n,0)-qpower(n,1))).a;}
void build(int rot,int lt,int rt)
{
tag[rot]=(Node){0,1};
if(lt==rt){a[rot]=(Node){F(w[lt]),F(w[lt]+1)};return;}
int mid=(lt+rt)>>1;
build(rot<<1,lt,mid),build(rot<<1|1,mid+1,rt);
a[rot]=a[rot<<1]+a[rot<<1|1];
}
void upd(int rot,Node x){a[rot]=a[rot]*x,tag[rot]=tag[rot]*x;}
void pushdown(int rot)
{
if(tag[rot].s1!=0||tag[rot].s2!=1)
{
Node t=tag[rot];tag[rot]=(Node){0,1};
upd(rot<<1,t),upd(rot<<1|1,t);
}
}
void update(int rot,int lt,int rt,int lq,int rq,Node x)
{
if(lt>=lq&&rt<=rq){upd(rot,x);return;}
int mid=(lt+rt)>>1;pushdown(rot);
if(lq<=mid)update(rot<<1,lt,mid,lq,rq,x);
if(rq>mid)update(rot<<1|1,mid+1,rt,lq,rq,x);
a[rot]=a[rot<<1]+a[rot<<1|1];
}
int query(int rot,int lt,int rt,int lq,int rq)
{
if(lt>=lq&&rt<=rq)return a[rot].s1;
int mid=(lt+rt)>>1;pushdown(rot);
if(rq<=mid)return query(rot<<1,lt,mid,lq,rq);
else if(lq>mid)return query(rot<<1|1,mid+1,rt,lq,rq);
else return (query(rot<<1,lt,mid,lq,mid)+query(rot<<1|1,mid+1,rt,mid+1,rq))%mod;
}
int main()
{
scanf("%d%d",&n,&m);make();
for(int i=1;i<=n;i++)scanf("%d",w+i);
build(1,1,n);
for(int i=1;i<=m;i++)
{
int opt,l,r,x;
scanf("%d%d%d",&opt,&l,&r);
if(opt==1)scanf("%d",&x),update(1,1,n,l,r,(Node){F(x),F(x+1)});
else printf("%d\n",(query(1,1,n,l,r)+mod)%mod);
}
}
后记
不难发现 f n + m f_{n+m} fn+m 和 f n , f n − 1 f_{n},f_{n-1} fn,fn−1 有 线性变换 关系,可以考虑用矩阵维护 f a i , f a i − 1 f_{a_i},f_{a_i-1} fai,fai−1 ,转移矩阵就是斐波那契的递推矩阵。