描述
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);
}
}
}