codeforces C. DZY Loves Fibonacci Numbers (数论+线段树)

C. DZY Loves Fibonacci Numbers
time limit per test
4 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

In mathematical terms, the sequence Fn of Fibonacci numbers is defined by the recurrence relation

F1 = 1; F2 = 1; Fn = Fn - 1 + Fn - 2 (n > 2).

DZY loves Fibonacci numbers very much. Today DZY gives you an array consisting of n integers: a1, a2, ..., an. Moreover, there are mqueries, each query has one of the two types:

  1. Format of the query "l r". In reply to the query, you need to add Fi - l + 1 to each element ai, where l ≤ i ≤ r.
  2. Format of the query "l r". In reply to the query you should output the value of  modulo 1000000009 (109 + 9).

Help DZY reply to all the queries.

Input

The first line of the input contains two integers n and m (1 ≤ n, m ≤ 300000). The second line contains n integersa1, a2, ..., an (1 ≤ ai ≤ 109) — initial array a.

Then, m lines follow. A single line describes a single query in the format given in the statement. It is guaranteed that for each query inequality 1 ≤ l ≤ r ≤ n holds.

Output

For each query of the second type, print the value of the sum on a single line.

Examples
input
4 4
1 2 3 4
1 1 4
2 1 4
1 2 4
2 1 3
output
17
12
Note

After the first query, a = [2, 3, 5, 7].

For the second query, sum = 2 + 3 + 5 + 7 = 17.

After the third query, a = [2, 4, 6, 9].

For the fourth query, sum = 2 + 4 + 6 = 12.



题解:数论+线段树。
 利用斐波那契数列的两个性质,若一个数列满足F(1)=a,F(2)=b,F(i)=F(i-2)+F(i-1),则有F(n)=a*Fib(n-2)+b*Fib(n-1),且F的前N项和等于F(n+2)-F(2)。
先预处理出斐波那契数列的前n+2项。
区间操作相当于对一个区间加上一个一一对应的区间,我们可以利用上面的两条性质,用F(n+2)=a*fib(n)+b*fib(n+1)求出F(n+2),再利用F(n+2)-F(2)求出加给当前区间的总值,更新区间和数组。
我们发现对于一个区间我们需要维护第一项和第二项的值,因为每次加的数列都满足F(n)=F(n-1)+F(n-2)的性质,所以可以将首项和第二项相加,再用公式直接计算区间和。
在下放标记的时候,左子树直接加父亲的首项和第二项的标记即可,右子树需要计算父亲的F(mid+1-l+1),F(mid+2-l+1)再累加上去。
<span style="font-size:18px;">#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 1200003
#define p 1000000009
#define LL long long
using namespace std;
int n,m;
LL delta[N],delta1[N],a[N],b[N],tr[N],f[N],val[N],sum[N];
void update(int now)
{
	tr[now]=tr[now<<1]+tr[now<<1|1];
}
void build(int now,int l,int r)
{
	if (l==r)
	 {
	 	tr[now]=val[l];
	 	return;
	 }
	int mid=(l+r)/2;
	build(now<<1,l,mid);
	build(now<<1|1,mid+1,r);
	tr[now]=tr[now<<1]+tr[now<<1|1];
}
LL F(int x,LL a,LL b)
{
	return (a%p*f[x-2]%p+b%p*f[x-1]%p)%p;
}
LL calc(LL a,LL b,int x)
{
	return ((F(x+2,a,b)-b%p)%p+p)%p;
}
LL solve(LL a,LL b,LL x)
{
	return (a%p*f[x-2]%p+b%p*f[x-1]%p)%p;
}
void pushdown(int now,int l,int r)
{
	int mid=(l+r)/2;
	if (a[now]||b[now])
	{
		a[now<<1]=(a[now<<1]+a[now])%p; b[now<<1]=(b[now<<1]+b[now])%p;
		if (mid-l+1==1)  b[now<<1]=0;
		tr[now<<1]+=calc(a[now],b[now],mid-l+1)%p; tr[now<<1]%=p;
	    LL t=solve(a[now],b[now],mid+1-l+1)%p;
		LL t1=solve(a[now],b[now],mid+2-l+1)%p;
		a[now<<1|1]=(a[now<<1|1]+t)%p; 
		b[now<<1|1]=(b[now<<1|1]+t1)%p; 
		if (r-mid==1) t1=0;
		tr[now<<1|1]+=calc(t,t1,r-mid); tr[now<<1|1]%=p;
		a[now]=0; b[now]=0;
	}
}
void qjchange(int now,int l,int r,int ll,int rr)
{
	if (ll<=l&&r<=rr)
	{
		a[now]=(a[now]+f[l-ll+1])%p; b[now]=(b[now]+f[l-ll+2])%p;
		LL t=f[l-ll+1]%p;  LL t1=f[l-ll+2]%p;
		if (r-l+1==1) t1=0;
		tr[now]=(tr[now]+calc(t,t1,r-l+1))%p;// cout<<now<<" "<<l<<" "<<r<<" "<<tr[now]<<endl; 
		return;
	}
	int mid=(l+r)/2;
	pushdown(now,l,r);
	if (ll<=mid) qjchange(now<<1,l,mid,ll,rr);
	if (rr>mid) qjchange(now<<1|1,mid+1,r,ll,rr);
	update(now);
}
LL qjsum(int now,int l,int r,int ll,int rr)
{
	if (ll<=l&&r<=rr) return tr[now]%p;
	int mid=(l+r)/2;
	pushdown(now,l,r);
	LL ans=0;
	if (ll<=mid) ans=(ans+qjsum(now<<1,l,mid,ll,rr))%p;
	if (rr>mid) ans=(ans+qjsum(now<<1|1,mid+1,r,ll,rr))%p;
	return ans%p;
}
int main()
{
	//freopen("a.in","r",stdin);
	//freopen("my.out","w",stdout);
	scanf("%d%d",&n,&m);
	f[1]=1; f[2]=1;
	for (int i=3;i<=n+10;i++)  f[i]=(f[i-1]+f[i-2])%p;
	for (int i=1;i<=n;i++) scanf("%d",&val[i]);
	build(1,1,n);
	for (int i=1;i<=m;i++)
	{
		int opt,x,y; scanf("%d%d%d",&opt,&x,&y);
		if (opt==1)  qjchange(1,1,n,x,y);
		else printf("%I64d\n",qjsum(1,1,n,x,y)%p);
	}
}</span>


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值