链接:https://ac.nowcoder.com/acm/contest/874/B
时间限制:C/C++ 5秒,其他语言10秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
HRY is a pupil. He has just learned about Fibonacci sequence recently, and he made some new sequences :
f i c ( n ) = ∑ i = 1 n f i b ( i ) fic(n)=\sum_{i=1}^nfib(i) fic(n)=i=1∑nfib(i) f i d ( n ) = ∑ i = 1 n f i c ( i ) fid(n)=\sum_{i=1}^nfic(i) fid(n)=i=1∑nfic(i)
fib(i) means the ith element in the Fibonacci sequence :
f i b ( 1 ) = f i b ( 2 ) = 1 , f i b ( i ) = f i b ( i − 1 ) + f i b ( i − 2 ) , i ≥ 3 fib(1)=fib(2)=1,fib(i)=fib(i−1)+fib(i−2),i≥3 fib(1)=fib(2)=1,fib(i)=fib(i−1)+fib(i−2),i≥3
At first this problem is to calculate fid(n), but that is too easy. So after discussing with 10256, they changed the problem:
Given a positive integer array
a
1
,
a
2
,
.
.
.
,
a
n
,
a_1,a_2,...,a_n,
a1,a2,...,an, perform the following two types of operations:
- Given l,r,x, for all l ≤ i ≤ r l≤i≤r l≤i≤r, execute a i = a i + x a_i=a_i+x ai=ai+x.
- Given l,r, calculate ( ∑ i = l r f i d ( a i ) ) % 100000007. (\sum_{i=l}^rfid(a_i))\%100000007. (∑i=lrfid(ai))%100000007.
输入描述:
The first line of the input contains an integer n, indicating the length of the array.
The second line contains n positive integers separated by spaces, indicating a 1 , a 2 , . . . , a n − 1 , a n . a_1,a_2,...,a_{n−1},a_n. a1,a2,...,an−1,an.
The third line contains an integer Q, indicating the number of operations.
For the next Q lines, it is either 1 1 1 l l l r r r x x x, indicating the first type of operation, or 2 2 2 l l l r r r, indicating the second type of operation.
All values in input are integers.
1
≤
n
,
Q
≤
100000
,
1
≤
a
i
,
x
≤
1
0
9
,
1
≤
l
≤
r
≤
n
.
1≤n,Q≤100000,1≤a_i,x≤10^9,1≤l≤r≤n.
1≤n,Q≤100000,1≤ai,x≤109,1≤l≤r≤n.
输出描述:
For each operation of second type, output
(
∑
i
=
l
r
f
i
d
(
a
i
)
)
%
100000007.
(\sum_{i=l}^rfid(a_i))\%100000007.
(∑i=lrfid(ai))%100000007.
示例1
输入
3
1 2 3
6
2 1 3
1 1 3 1
2 1 3
1 1 1 2
1 2 2 3
2 1 3
输出
11
24
74
思路:由斐波那契数列性质 f 1 + f 2 + f 3 + . . + f n = f n + 2 − 1 f_1+f_2+f_3+..+f_n=f_{n+2}-1 f1+f2+f3+..+fn=fn+2−1,可得 f i d ( n ) = f n + 4 − n − 3 fid(n)=f_{n+4}-n-3 fid(n)=fn+4−n−3.
知道了
f
i
d
(
n
)
fid(n)
fid(n)的公式,那么
f
i
d
(
n
)
fid(n)
fid(n)可以由矩阵快速幂求得,即:
[
f
1
0
f
0
−
1
0
0
0
0
0
0
0
0
0
0
0
0
]
∗
[
1
0
1
0
0
2
0
1
1
0
0
0
0
−
1
0
0
]
n
+
3
=
[
f
n
+
4
n
+
3
f
n
+
3
n
+
2
0
0
0
0
0
0
0
0
0
0
0
0
]
\left[ \begin{matrix} f_1& 0&f_0&-1\\ 0& 0&0&0\\ 0& 0&0&0\\ 0&0&0&0 \end{matrix} \right]* \left[ \begin{matrix} 1& 0&1&0\\ 0& 2&0&1\\ 1& 0&0&0\\ 0&-1&0&0 \end{matrix} \right] ^{n+3}= \left[ \begin{matrix} f_{n+4}& n+3&f_{n+3}&n+2\\ 0& 0&0&0\\ 0& 0&0&0\\ 0&0&0&0 \end{matrix} \right]
⎣⎢⎢⎡f10000000f0000−1000⎦⎥⎥⎤∗⎣⎢⎢⎡1010020−110000100⎦⎥⎥⎤n+3=⎣⎢⎢⎡fn+4000n+3000fn+3000n+2000⎦⎥⎥⎤
现在就是考虑区间求和,由于有区间修改,所以我们可以建立一个线段树,但线段树节点存储的是伴随矩阵 A = [ 1 0 1 0 0 2 0 1 1 0 0 0 0 − 1 0 0 ] A=\left[ \begin{matrix} 1& 0&1&0\\ 0& 2&0&1\\ 1& 0&0&0\\ 0&-1&0&0 \end{matrix} \right] A=⎣⎢⎢⎡1010020−110000100⎦⎥⎥⎤
即每个叶节点存储的是
A
a
i
A^{a_i}
Aai。
那么区间修改的的时候只需要在区间
[
L
,
R
]
[L,R]
[L,R]乘上相应的
A
x
A^x
Ax即可,打上懒惰标记。
#include<bits/stdc++.h>
using namespace std;
const int MAX=1e5+10;
const int MOD=100000007;
typedef long long ll;
struct M
{
int a[4][4];
M()
{
memset(a,0,sizeof a);
for(int i=0;i<4;i++)a[i][i]=1;
}
}now;
M operator+(const M&A,const M&B)
{
M C;
for(int i=0;i<4;i++)
for(int j=0;j<4;j++)C.a[i][j]=(A.a[i][j]+B.a[i][j])%MOD;
return C;
}
M operator*(const M&A,const M&B)
{
M C;memset(C.a,0,sizeof C.a);
for(int i=0;i<4;i++)
for(int j=0;j<4;j++)
for(int k=0;k<4;k++)
{
C.a[i][j]+=1ll*A.a[i][k]*B.a[k][j]%MOD;
C.a[i][j]%=MOD;
}
return C;
}
M POW(int n)
{
M a,res;
memset(res.a,0,sizeof res.a);
memset(a.a,0,sizeof a.a);
for(int i=0;i<4;i++)res.a[i][i]=1;
a.a[0][0]=a.a[0][2]=1;
a.a[1][1]=2,a.a[1][3]=1;
a.a[2][0]=1;
a.a[3][1]=MOD-1;
while(n)
{
if(n&1)res=res*a;
a=a*a;
n>>=1;
}
return res;
}
struct lenka
{
int l,r;
M a,lazy;
}A[MAX<<2];
void build(int k,int l,int r)
{
A[k].l=l,A[k].r=r;
A[k].lazy=M();
if(l==r)
{
int x;
scanf("%d",&x);
A[k].a=POW(x+3);
return;
}
build(2*k,l,(l+r)/2);
build(2*k+1,(l+r)/2+1,r);
A[k].a=A[2*k].a+A[2*k+1].a;
}
void add(int k,int x,int y,M z)
{
if(x==A[k].l&&y==A[k].r)
{
A[k].a=A[k].a*z;
if(x!=y)A[k].lazy=A[k].lazy*z;
return;
}
if(y<=A[2*k].r)add(2*k,x,y,z);
else if(x>=A[2*k+1].l)add(2*k+1,x,y,z);
else
{
add(2*k,x,A[2*k].r,z);
add(2*k+1,A[2*k+1].l,y,z);
}
A[k].a=A[k].lazy*(A[2*k].a+A[2*k+1].a);
}
M ask(int k,int x,int y)
{
if(x==A[k].l&&y==A[k].r)return A[k].a;
if(y<=A[2*k].r)return ask(2*k,x,y);
else if(x>=A[2*k+1].l)return ask(2*k+1,x,y);
return A[k].lazy*(ask(2*k,x,A[2*k].r)+ask(2*k+1,A[2*k+1].l,y));
}
int main()
{
memset(now.a,0,sizeof now.a);
now.a[0][0]=1;
now.a[0][3]=MOD-1;
int n;
cin>>n;
build(1,1,n);
int m;
cin>>m;
while(m--)
{
int op,x,y,z;
scanf("%d%d%d",&op,&x,&y);
if(op==1)
{
scanf("%d",&z);
add(1,x,y,POW(z));
}
else
{
M ans=now*ask(1,x,y);
printf("%d\n",(ans.a[0][0]-ans.a[0][1]+MOD)%MOD);
}
}
return 0;
}