zoukankan      html  css  js  c++  java
  • CF446C DZY Loves Fibonacci Numbers

    题目大意是维护一个序列,规定区间加法为给区间每个数加上对应的 (fibnacci) 数列的一项。

    我刚拿到题想到了直接下传标记、标记永久化等一堆显然错误的算法。错误原因是标记不能合并,所以时间复杂度得不到保证。

    看到一种非常神仙的做法,用到了 (fibnacci) 数列的一个性质:

    [f_{n+m}=f_{n+1} imes f_m+f_n imes f_{m-1} ]

    其中如果 (f) 出现了负的下标也没有关系,直接按 (f_i=f_{i+2}-f_{i+1}) 推就行。

    然后我们发现对一个区间([l,r])的操作,每个位置 (x) 增加的值为 (f_{x-l+1}),直接拆开:

    [f_{x-l+1}=f_{x+1} imes f_{-l+1} + f_{x} imes f_{-l} ]

    这样一次操作对每个点的影响就变成了常数( (f_{-l+1})(f_{-l}) )乘上一个和自身有关的东西( (f_x)(f_{x+1}) ),那么我们直接维护两个标记分别记录贡献就好了。

    代码:

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<iostream>
    
    using namespace std;
    
    typedef long long LL;
    const int N=300009,M=1000000009;
    int n,m,a[N];
    LL sum[N*4],f[N*2],sf[N*2],add1[N*4],add2[N*4];
    
    void build(int k,int l,int r)
    {
    	if(l==r)
    	{
    		sum[k]=a[l];
    		return;
    	}
    	int mid=l+r>>1;
    	build(k<<1,l,mid);
    	build(k<<1|1,mid+1,r);
    	sum[k]=sum[k<<1]+sum[k<<1|1];
    }
    
    int t(int x) { return x+300000; }
    
    void init()
    {
    	scanf("%d %d",&n,&m);
    	for (int i=1;i<=n;i++)
    		scanf("%d",&a[i]);
    	build(1,1,n);
    	f[t(1)]=1;
    	for (int i=2;i<=n+1;i++)
    		f[t(i)]=(f[t(i-1)]+f[t(i-2)])%M;
    	for (int i=0;i>=-n-1;i--)
    		f[t(i)]=(f[t(i+2)]-f[t(i+1)])%M;
    	for (int i=1;i<=n+1;i++)
    		sf[t(i)]=(f[t(i)]+sf[t(i-1)])%M;
    }
    
    void Add(int k,int l,int r,LL x,LL y)
    {
    	sum[k]=(sum[k]+x*(sf[t(r+1)]-sf[t(l)])%M+y*(sf[t(r)]-sf[t(l-1)])%M)%M;
    	add1[k]=(add1[k]+x)%M,add2[k]=(add2[k]+y)%M;
    }
    
    void push_down(int k,int l,int r,int mid)
    {
    	Add(k<<1,l,mid,add1[k],add2[k]);
    	Add(k<<1|1,mid+1,r,add1[k],add2[k]);
    	add1[k]=add2[k]=0;
    }
    
    void Modify(int k,int l,int r,int x,int y)
    {
    	if(l>=x&&r<=y)
    	{
    		Add(k,l,r,f[t(1-x)],f[t(-x)]);
    		return;
    	}
    	int mid=l+r>>1;
    	push_down(k,l,r,mid);
    	if(mid>=x)
    		Modify(k<<1,l,mid,x,y);
    	if(mid<y)
    		Modify(k<<1|1,mid+1,r,x,y);
    	sum[k]=(sum[k<<1]+sum[k<<1|1])%M;
    }
    
    LL Querysum(int k,int l,int r,int x,int y)
    {
    	if(l>=x&&r<=y)
    		return sum[k];
    	int mid=l+r>>1;
    	LL res=0;
    	push_down(k,l,r,mid);
    	if(mid>=x)
    		res=(res+Querysum(k<<1,l,mid,x,y))%M;
    	if(mid<y)
    		res=(res+Querysum(k<<1|1,mid+1,r,x,y))%M;
    	return res;
    }
    
    void work()
    {
    	while(m--)
    	{
    		int opt,x,y;
    		scanf("%d %d %d",&opt,&x,&y);
    		if(opt==1)
    			Modify(1,1,n,x,y);
    		else
    			printf("%lld
    ",(Querysum(1,1,n,x,y)+M)%M);
    	}
    }
    
    int main()
    {
    	init();
    	work();
    	return 0;
    }
    
    
    由于博主比较菜,所以有很多东西待学习,大部分文章会持续更新,另外如果有出错或者不周之处,欢迎大家在评论中指出!
  • 相关阅读:
    知识图谱概述
    架构浅谈之 MVC
    windows下安装Levenshtein
    python安装pyahocorasick遇到error: Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C++ Build Tools":
    解决pycharm中: OSError: [WinError 1455] 页面文件太小,无法完成操作 的问题
    win10下yolov5的cpu和gpu环境搭建
    使用neo4j工具导入知识图谱
    【2021.03.07】看论文神器知云文献翻译、百度翻译API申请、机器学习术语库
    【2021.03.06】智能家居之双控开关改单控开关+无线开关
    【2021.03.06】IMAP协议与POP3协议的对比、在Gmail中添加QQ邮箱IMAP同步
  • 原文地址:https://www.cnblogs.com/With-penguin/p/13190820.html
Copyright © 2011-2022 走看看