1. 程式人生 > >ZYH的斐波那契數列【線段樹動態開點+矩陣快速冪求斐波那契】

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 logn)。【n為update次數】第一次update肯定是新建logn個節點,後面update的時候有些點是已存在的,有些點是空的,新建的點數就應該小於等於logn。

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