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;
}

由于博主比较菜,所以有很多东西待学习,大部分文章会持续更新,另外如果有出错或者不周之处,欢迎大家在评论中指出!
原文地址:https://www.cnblogs.com/With-penguin/p/13190820.html