zoukankan      html  css  js  c++  java
  • CF446C DZY Loves Fibonacci Numbers 线段树 + 数学

    有两个性质需要知道:

    $1.$ 对于任意的 $f[i]=f[i-1]+f[i-2]$ 的数列,都有 $f[i]=fib[i-2] imes f[1]+fib[i-1] imes f[2]$

    其中 $fib[i]$ 为第 $i$ 项斐波那契数列.

    $2$. 对于任意满足上述条件的数列,都有 $sum_{i=1}^{n}f[i]=f[n+2]-f[2]$

    $3.$ 任意两断满足上述条件的数列每一项依次叠加,依然满足 $g[i]=g[i-1]+g[i-2]$,且上述两个性质都满足.

    $4.$ 任何一段斐波那契数列也满足上述所有性质.

    有了上述预备知识后,再考虑这道题:

    我们用线段树来维护区间和,线段树上每个节点维护 $3$ 个信息,为 $sum,f1,f2$

    即节点所维护的区间和,以及该节点及线段树中区间要加上一个前两项为 $f1,f2$ 的上述递推数列.

    那么,我们只需考虑如何下传标记,如何查询即可.

    假设当前节点已经有了 $f1,f2$,那么将标记下传给左子树是轻松的:直接下传即可,区间和的贡献可按照上述公式 $O(1)$ 求出.

    而如果要下传给右儿子的话就不能直接传了,因为右儿子区间开头的两项并不是 $f1,f2$.

    而根据上述三条性质,我们知道斐波那契数列的任何一段也是斐波那契数列.

    所以,直接算出右儿子的 $f1,f2$ 即 $f1 imes fib[mid-l]+f2 imes fib[mid-l+1]$ 与 $f1 imes fib[mid-l+1]+f2 imes fib[mid-l+2]$

    然后还知道 $f1,f2$ 都满足叠加性,所以直接叠加到左右儿子的 $f1,f2$ 上即可.

    #include <bits/stdc++.h> 
    #define N 400004   
    #define LL long long   
    #define lson now<<1 
    #define rson now<<1|1 
    #define setIO(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)  
    using namespace std;  
    const LL mod=1000000009;            
    int n,m;                     
    LL fib[N<<1],sum[N<<1];    
    struct node 
    {
    	LL f1,f2,sum;   
    	int l,r,len;      
    }t[N<<2];  
    void build(int l,int r,int now) 
    { 
    	t[now].l=l; 
    	t[now].r=r; 
    	t[now].len=r-l+1;  
    	if(l==r) return ;  
    	int mid=(l+r)>>1;      
    	if(l<=mid)    build(l,mid,lson); 
    	if(r>mid)     build(mid+1,r,rson);      
    }  
    void mark(int now,LL f1,LL f2) 
    {    
    	(t[now].f1+=f1)%=mod; 
    	(t[now].f2+=f2)%=mod;        
    	(t[now].sum+=f1*fib[t[now].len]%mod+f2*fib[t[now].len+1]%mod-f2+mod)%=mod;        
    }   
    void pushup(int now) 
    {
    	t[now].sum=(t[lson].sum+t[rson].sum)%mod;               
    } 
    void pushdown(int now) 
    {
    	if(t[now].f1==0&&t[now].f2==0) return;   
    	int mid=(t[now].l+t[now].r)>>1;             
    	mark(lson,t[now].f1,t[now].f2);    
    	if(t[now].r>mid)   
    	mark(rson,t[now].f1*fib[t[lson].len-1]%mod+t[now].f2*fib[t[lson].len]%mod,t[now].f1*fib[t[lson].len]%mod+t[now].f2*fib[t[lson].len+1]%mod);     
    	t[now].f1=t[now].f2=0;    
    }
    void update(int l,int r,int now,int L,int R) 
    { 
    	if(l>=L&&r<=R) 
    	{            
    		mark(now,fib[l-L+1],fib[l-L+2]);           
    		return;       
    	}    
    	pushdown(now); 
    	int mid=(l+r)>>1;  
    	if(L<=mid)     update(l,mid,lson,L,R); 
    	if(R>mid)      update(mid+1,r,rson,L,R);    
    	pushup(now);      
    }    
    LL query(int l,int r,int now,int L,int R) 
    { 
    	if(l>=L&&r<=R)     
    	{    
    		return t[now].sum;   
    	}
    	pushdown(now); 
    	int mid=(l+r)>>1;   
    	LL re=0ll;  
    	if(L<=mid)    re+=query(l,mid,lson,L,R);   
    	if(R>mid)     re+=query(mid+1,r,rson,L,R);   
    	return re%mod; 
    }
    int main() 
    { 
    	// setIO("input"); 
    	int i,j; 
    	scanf("%d%d",&n,&m); 
    	fib[1]=fib[2]=1; 
    	for(i=3;i<N;++i)    fib[i]=(fib[i-1]+fib[i-2])%mod;     
    	for(i=1;i<=n;++i)    scanf("%lld",&sum[i]), (sum[i]+=sum[i-1])%=mod;    
    	build(1,n,1);      
    	for(i=1;i<=m;++i) 
    	{   
    		int opt,l,r;     
    		scanf("%d%d%d",&opt,&l,&r);        
    		if(opt==1) update(1,n,1,l,r); 
    		else printf("%lld
    ",(query(1,n,1,l,r)+sum[r]-sum[l-1]+mod*2)%mod);    
    	}    
    	return 0;   
    }
    

      

  • 相关阅读:
    软件需求分析——阅读笔记
    第二次冲刺阶段 tenth day
    第16周周总结
    第二次冲刺阶段 ninth day
    判断各种数据类型的方法总结
    vue中8种组件通信方式
    字符串常用方法总结
    JS中轻松遍历对象属性的几种方式
    fetch请求和ajax请求
    js 文件下载,当前页下载,新标签下载____后端返回 GET/POST 文件流,下载文件
  • 原文地址:https://www.cnblogs.com/guangheli/p/11798397.html
Copyright © 2011-2022 走看看