[CF718C]Sasha and Array

280 篇文章 1 订阅
137 篇文章 1 订阅

题目

传送门 to luogu

思路

注意到 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=f2fn1+f1fn2(1)

然后把 f n − 1 f_{n-1} fn1 展开成 f n − 2 + f n − 3 f_{n-2}+f_{n-3} fn2+fn3 得到

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=f2fn2+f2fn3+f1fn2

由于 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=f3fn2+f2fn3(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=fmfnm+1+fm1fnm

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+fm1fn(3)

又因为 f m − 1 = f m + 1 − f m f_{m-1}=f_{m+1}-f_m fm1=fm+1fm ,进一步展开为

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+1fnfmfn

把这个式子用到题目里去,就可以看出

∑ 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=lrfai+x=fxi=lrfai+1+fx+1i=lrfaifxi=lrfai

所以我们只需要求区间的 ∑ 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(215 )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,fn1线性变换 关系,可以考虑用矩阵维护 f a i , f a i − 1 f_{a_i},f_{a_i-1} fai,fai1 ,转移矩阵就是斐波那契的递推矩阵。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值