CF446C DZY Loves Fibonacci Numbers

发布时间 2023-09-08 10:34:00作者: NBest

2023-07-18 20:49:31

思路:

一开始的思路是每次存两个值,因为任意两个斐波那契数列合并之后仍然满足斐波那契的基本性质 \(f[x]=f[x-1]+f[x-2]\)

但是发现这样子每次修改直接的总和得暴力递推求,复杂度爆炸。
为了解决这个突破口,稍微借鉴了一下题解的斐波那契数列性质。

由斐波那契数列性质:\(f(n+m)=f(n+1)*f(m)+f(n)*f(m-1)\)

对于区间 \([l,r]\) 的数,\(x\) 为其中一个位置,则需要在 \(x\) 上加上的为 \(f(x-l+1)\ \ (x<-[l,r])\)

我们将x和l分离开可得:\(f[x+(1-l)]=f[x+1]*f[1-l]+f[x]*f[-l]\)

我们发现,我们可以通过前缀和的方式求出整个区间中 \(f[x]和f[x+1]\) 的和,而每次修改过程中 \(f[1-l]\)\(f[-l]\) 是不变的,可以直接用懒标记下放。
负数的斐波那契数列可以用倒推的方式求得。

由此解决了总和无法 \(O(1)\) 求得的瓶颈

注:在处理pre[]数组的时候要注意略大于n(比如调成n+3),因为会访问到r+1,此时可能会爆

Code

typedef long long ll;
const int mod=1e9+9;
#define mid (l+r>>1)
int n,m;
ll tr[1200005],pre[300005],tag1[1200005],tag2[1200005];//tag1:f[1-l],tagf2:f[-l]
unordered_map<int,ll>f;
inline void pushup(int root){
    tr[root]=(tr[root<<1]+tr[root<<1|1])%mod;
}
void build(int root,int l,int r){
    if(l==r){
        tr[root]=read();
        return;
    }
    build(root<<1,l,mid),build(root<<1|1,mid+1,r);
    pushup(root);
}
inline void pupudown(int root,int l,int r,int a1,int a2){
    tag1[root]=(tag1[root]+a1)%mod;
    tag2[root]=(tag2[root]+a2)%mod;
    tr[root]=(tr[root]+a1*(((pre[r+1]-pre[l])%mod+mod)%mod)%mod+a2*(((pre[r]-pre[l-1])%mod+mod)%mod)%mod)%mod;
}
inline void pushdown(int root,int l,int r){
    if(tag1[root]||tag2[root]){
        pupudown(root<<1,l,mid,tag1[root],tag2[root]);
        pupudown(root<<1|1,mid+1,r,tag1[root],tag2[root]);
        tag1[root]=tag2[root]=0;
    }
}
void update(int root,int l,int r,int x,int y){
    if(x<=l&&r<=y){
        pupudown(root,l,r,f[1-x],f[-x]);
        return;
    }
    pushdown(root,l,r);
    if(mid>=x)update(root<<1,l,mid,x,y);
    if(mid<y)update(root<<1|1,mid+1,r,x,y);
    pushup(root);
}
ll query(int root,int l,int r,int x,int y){
    if(x<=l&&r<=y){
        return tr[root];
    }
    ll ans=0;
    pushdown(root,l,r);
    if(mid>=x)ans=query(root<<1,l,mid,x,y)%mod;
    if(mid<y)ans=(ans+query(root<<1|1,mid+1,r,x,y))%mod;
    pushup(root);
    return ans;
}
int main(){
    n=read(),m=read();
    build(1,1,n);//f[1]=f[0]+f[-1]->f[-1]=f[1]-f[0]  
    f[0]=0;
    f[1]=pre[1]=1;
    for(int i=2;i<=n+3;i++){
        f[i]=(f[i-1]+f[i-2])%mod;
        pre[i]=(pre[i-1]+f[i])%mod;
    }
    for(int i=-1;i>=-n-3;--i){
        f[i]=((f[i+2]-f[i+1])%mod+mod)%mod;
    }
    while(m--){
        int opt=read(),l=read(),r=read();
        if(opt&1){
            update(1,1,n,l,r);
        }else{
            printf("%lld\n",query(1,1,n,l,r));
        }
    }
}