CF446C(线段树+斐波那契)

CF446C(线段树+斐波那契数列)

CF链接

洛谷链接

题目大意:

区间加斐波那契数列,区间求和

分析:

一眼鉴定为线段树

难点在于如何打标记,合并和传递标记

对于斐波那契数列有几个性质(下方会给出解释):

性质1:

两个长度相等的斐波那契数列相加之后仍然可以满足斐波那契 \(f_i=f_{i-1}+f_{i-2}\) 的性质(仍然是一个广义斐波那契数列)。

性质2:

通过维护每个斐波那契数列的第一第二项就可以 \(O(1)\) 的获得任意一项的数值和任意 \(i\) 项的前缀和.


对于性质1:

假设有两个斐波那契数列相加
\(1,2,3,5\)
\(13,21,34,55\)
结果为
\(14,23,37,60\)
其任然是一个广义斐波那契数列

对于性质2:

将斐波那契数列每一项进行拆解
\(f_1=f_1\)
\(f_2=f_2\)
\(f_3=f_2+f_1\)
\(f_4=f_3+f_2=2*f_2+f_1\)
\(……\)
\(f_i=f_{i-1}*f_2+f_{i-2}*f_1\)
斐波那契数列每一项都能用第一第二项来表示。

对于任意 \(i\) 的前缀和
\(s_1=a,s_2=b\)
\(s_i=a*s_{i-2}+b*s_{i-1}\)

维护标记:

维护某一段区间的和,我们可以在线段树每个节点上维护两个数 \(a,b\) 表示第一个数加了多少,第二个数加了多少。求区间和时,只需要套用上面的式子。

代码:

#include<bits/stdc++.h>
#define int  long long
#define MAXN 300860
#define mod 1000000009
using namespace std;

struct T{
    int l,r,node,len,lazy,val;
}t[MAXN<<2];

int n,m,a[MAXN],f[MAXN],op;


void updata(int node){
    t[node].val=(t[node<<1].val+t[node<<1|1].val)%mod;
}


void build(int l,int r,int node){
    if(l==r){
        t[node].val=a[l];
        return ;
    }
    int mid=(l+r)>>1;
    build(l,mid,node<<1);
    build(mid+1,r,node<<1|1);
    updata(node);
}

int cal(int a,int b,int k){
    if(k==1)return a;
    if(k==2)return b;
    return (a*f[k-2]%mod+b*f[k-1]%mod)%mod;

}
int getsum(int a,int b,int k){
    if(k==1)return a;
    if(k==2)return(a+b)%mod;
    return (cal(a,b,k+2)-b+mod)%mod;
}

void paint(int node,int l,int r, int aa,int bb){
    t[node].l=(t[node].l+aa)%mod;
    t[node].r=(t[node].r+bb)%mod;
    t[node].val+=getsum(aa,bb,r-l+1);
    t[node].val%=mod;
}

void pushdown(int node,int l,int r,int mid){
    if(!t[node].l)return ;
    paint(node<<1,l,mid,t[node].l,t[node].r);
    int aa=cal(t[node].l,t[node].r,mid+1-l+1);
    int bb=cal(t[node].l,t[node].r,mid+2-l+1);
    paint(node<<1|1,mid+1,r,aa,bb);
    t[node].l=0;
	t[node].r=0;
}



void change(int l,int r,int node,int x,int y){
    if(x<=l&&r<=y){
        paint(node,l,r,f[l-x+1],f[l-x+2]);
        return ;
    }
    int mid=(l+r)>>1;
    pushdown(node,l,r,mid);
    if(x<=mid)change(l,mid,node<<1,x,y);
    if(mid<y)change(mid+1,r,node<<1|1,x,y);
    updata(node);
}

int ask(int l,int r,int node,int x,int y){
    if(x<=l&&r<=y){
        return t[node].val;
    }
    int mid=(l+r)>>1;
    int ans=0;
    pushdown(node,l,r,mid);
    if(x<=mid)ans+=ask(l,mid,node<<1,x,y);
    if(y>mid)ans+=ask(mid+1,r,node<<1|1,x,y);
    return ans%mod;
}



signed main(){
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);

    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    build(1,n,1);
    int x,y,k;
    f[1]=1;
    f[2]=1;
    
	for(int i=3;i<=n+9;i++){//求斐波那契数列 
        f[i]=(f[i-1]+f[i-2])%mod;
    }
    
    for(int i=1;i<=m;i++){
        cin>>op>>x>>y;
        if(op==1){
            change(1,n,1,x,y);
        }
        else{
        cout<<ask(1,n,1,x,y)<<endl;
        }

    }



    return 0;
}
posted @ 2022-09-04 20:10  DAIANZE  阅读(136)  评论(0编辑  收藏  举报