ZYH的斐波那契数列【线段树动态开点+矩阵快速幂求斐波那契】

描述

ZYH最近研究数列研究得入迷啦!

现在有一个斐波拉契数列(f[1]=f[2]=1,对于n>2有f[n]=f[n-1]+f[n-2]),

但是斐波拉契数列太简单啦,于是ZYH把它改成了斐波拉契的前缀和的数列{Si}(S[1]=1,对于n>1,有S[n]=S[n-1]+f[n]),接下来ZYH要在{Si}数列上面做一些操作。

1.修改: 将数列中下标在[l,r]区间内的数加上v

2.查询:询问此时数列第k项对1000000007取模后是多少。

你能帮他解决这两个操作吗?

输入

第一行,一个正整数,n,代表操作的次数。

接下来n行,先输入一个数p,若p=1,则再输入三个正整数l,r,v,代表将数列中下标在[l,r]区间内的数加上v;若p=2,则再输入一个正整数k, 询问此时数列第k项mod1000000007后是多少。

输出

对于每次询问,输出对应的整数,每个询问输出一行。

样例输入[复制]

5
1 1 10 2
2 1
2 2
2 3
2 4

样例输出[复制]

3
4
6
9

标签

ZYH 光华测试day1

数据范围

对于 10%的数据有 n ≤10^3, l ≤ r ≤ 10^3 , k≤10^3 , v≤10^3

对于 30%的数据有 n ≤10^5 , l ≤ r ≤ 10^6 , k≤10^6 , v≤10^6

对于 60%的数据有 n ≤10^5 , l ≤ r ≤ 10^6 , k≤10^9 , v≤10^9

对于 100%的数据有 n ≤10^5 , l ≤ r ≤ 10^9 , k≤10^9 , v≤10^9

 

 

区间修改和单点查询,可以利用差分。

查询的时候就是序列的前缀和【原序列全是0】加上对应的斐波那契数前缀和。其实两个东西是相对独立的。

先看这个前缀和吧:

由于查询的k范围为1e9,显然不能直接线段树或者树状数组。

观察到n只有1e5,那么可以用一个线段树动态开点来维护这个序列的前缀和。

这个动态的线段树就是:一开始是空的,update的时候,遇到一个空节点,就新建一个点。

查询的时候,如果遇到空节点,说明update没有更新到这个节点,那么直接返回0就行了。

空间差不多是O(n log 1e9)?

查k点值的时候就访问1到k的区间和就行了。

然后看看这个斐波那契数列:

求一个的时候就用矩阵快速幂就行了,但是这道题要求前缀和,也就是F[1]+F[2]+F[3]+......【其中F[1]=F[2]=1,题目中有说到】

给这个序列加上1,得到:1+F[1]+F[2]+F[3]+F[4]+......

把1看做F[1],把F[1]看做F[2],得到:F[1]+F[2]+F[2]+F[3]+F[4]+......

以斐波那契前六项为例:

F[1]+F[2]+F[3]+F[4]+F[5]+F[6]

=(F[1]+F[2])+F[2]+F[3]+F[4]+F[5]+F[6]-1

=F[3]+F[2]+F[3]+F[4]+F[5]+F[6]-1=(F[3]+F[2])+F[3]+F[4]+F[5]+F[6]-1

=F[4]+F[3]+F[4]+F[5]+F[6]-1=(F[4]+F[3])+F[4]+F[5]+F[6]-1

=F[5]+F[4]+F[5]+F[6]-1=(F[5]+F[4])+F[5]+F[6]-1

=F[6]+F[5]+F[6]-1=F[7]+F[6]-1=F[8]-1

通过这个过程,我们可以总结出:如果令S[i]为斐波那契数列前i项的前缀和,那么S[i]=F[i+2]-1。【在F[1]=F[2]=1的前提下】

那么这道题也就迎刃而解了。

【注意取模:保险起见,如果有减法,就加上一个mod再取模】

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod=1e9+7;
const ll maxn=1e6+10;
const ll INF=1e9+7;
ll n,p,l,r,v,k,t=1;
struct node{
	ll lc,rc,w;
	node(){lc=0,rc=0,w=0;}
}tr[maxn<<2];
void push_up(ll root){
	tr[root].w=(tr[tr[root].lc].w+tr[tr[root].rc].w)%mod;
	return;
}
void update(ll root,ll l,ll r,ll pos,ll W){
	if(l==r){
		tr[root].w=(tr[root].w+W)%mod;
		return;
	}
	ll mid=(l+r)>>1;
	if(pos<=mid){
		if(!tr[root].lc) tr[root].lc=++t;
		update(tr[root].lc,l,mid,pos,W);
	}
	else{
		if(!tr[root].rc) tr[root].rc=++t;
		update(tr[root].rc,mid+1,r,pos,W);
	}
	push_up(root);
}
ll query(ll L,ll R,ll l,ll r,ll root){
	if(!root) return 0;
	if(L<=l&&R>=r) return tr[root].w;
	ll mid=(l+r)>>1;
	ll ans=0;
	if(L<=mid) (ans+=query(L,R,l,mid,tr[root].lc))%=mod;
	if(R>mid) (ans+=query(L,R,mid+1,r,tr[root].rc))%=mod;
	return ans%mod;
}
struct matrix{
	ll a[3][3];
	matrix(ll t=0){
		if(t==0) a[1][1]=a[1][2]=a[2][1]=a[2][2]=0;
		if(t==1) a[1][1]=a[2][2]=1,a[1][2]=a[2][1]=0;
	}
	friend inline matrix operator +(matrix a,matrix b){
		matrix c;
		for(ll i=1;i<=2;++i)
			for(ll j=1;j<=2;++j)
				(c.a[i][j]=a.a[i][j]+b.a[i][j])%=mod;
		return c;
	}
	friend inline matrix operator *(matrix a,matrix b){
		matrix c;
		for(ll i=1;i<=2;++i)
			for(ll k=1;k<=2;++k)
				for(ll j=1;j<=2;++j)
					(c.a[i][j]+=(a.a[i][k]*b.a[k][j])%mod)%mod;
		return c;
	}
	friend inline matrix operator ^(matrix a,ll b){
		matrix c(1);
		for(;b;b>>=1,a=a*a) if(b&1) c=c*a;
		return c;
	}
}A,T;
void read(ll &x){
	x=0;char ch=getchar();
	while(ch>'9'||ch<'0') ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
}
void init(){
	T.a[1][1]=T.a[2][1]=1,T.a[1][2]=T.a[2][2]=0;
	A.a[1][1]=A.a[1][2]=A.a[2][1]=1,A.a[2][2]=0;
}
int main(){
	init(),read(n);
	while(n--){
		read(p);
		if(p==1){
			read(l),read(r),read(v);
			update(1,1,INF,l,v),update(1,1,INF,r+1,-v);
		}
		if(p==2){
			read(k);
			ll tmp=(A^(k+2)).a[1][2]-1;
			ll ans=query(1,k,1,INF,1);
			printf("%d\n",(ans+tmp+mod)%mod);
		}
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值